diff --git a/.github/workflows/E2E_On_PR.yml b/.github/workflows/E2E_On_PR.yml index bc9ff8c24..5116e0941 100755 --- a/.github/workflows/E2E_On_PR.yml +++ b/.github/workflows/E2E_On_PR.yml @@ -2,7 +2,7 @@ name: Cypress E2E Automation on: pull_request: types: [opened, reopened] - branches: [master] + branches: '**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -59,7 +59,17 @@ jobs: ${{ matrix.make }} - name: Running ${{ matrix.prestashop }} Cypress E2E tests - run: docker run -i -v $PWD:/e2e -w /e2e -e CYPRESS_baseUrl='${{ matrix.url }}' -e CYPRESS_MOLLIE_TEST_API_KEY=${{ secrets.MOLLIE_TEST_API_KEY }} -e CYPRESS_MOLLIE_TEST_PROFILE_ID=${{ secrets.MOLLIE_TEST_PROFILE_ID }} -e CYPRESS_TESTRAIL_RUN_ID=${{ matrix.TestRailID }} -e CYPRESS_TESTRAIL_DOMAIN=${{ secrets.TESTRAIL_DOMAIN }} -e CYPRESS_TESTRAIL_USERNAME=${{ secrets.TESTRAIL_USERNAME }} -e CYPRESS_TESTRAIL_PASSWORD='${{ secrets.TESTRAIL_PASSWORD }}' cypress/included:cypress-12.14.0-node-18.16.0-chrome-113.0.5672.92-1-ff-113.0-edge-113.0.1774.35-1 --spec "${{ matrix.test_spec }}" --browser chrome + run: | + export CYPRESS_baseUrl='${{ matrix.url }}' + export CYPRESS_MOLLIE_TEST_API_KEY=${{ secrets.MOLLIE_TEST_API_KEY }} + export CYPRESS_MOLLIE_TEST_PROFILE_ID=${{ secrets.MOLLIE_TEST_PROFILE_ID }} + export CYPRESS_TESTRAIL_RUN_ID=${{ matrix.TestRailID }} + export CYPRESS_TESTRAIL_DOMAIN='${{ secrets.TESTRAIL_DOMAIN }}' + export CYPRESS_TESTRAIL_USERNAME='${{ secrets.TESTRAIL_USERNAME }}' + export CYPRESS_TESTRAIL_PASSWORD='${{ secrets.TESTRAIL_PASSWORD }}' + npm install cypress@12.15.0 + npm ci + npx cypress run --spec "${{ matrix.test_spec }}" --browser chrome - name: Archive videos and screenshots if: ${{ always() }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 943ba6bd7..1d84d2267 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,11 +26,11 @@ jobs: - run: composer i - - name: Pull PrestaShop files (Tag 1.7.7.0) + - name: Pull PrestaShop files run: docker run -tid --rm -v ps-volume:/var/www/html -v $PWD/:/var/www/html/modules/mollie --name temp-ps prestashop/prestashop:1.7.8.1-7.4-apache - name: Run PHPunit - run: docker exec temp-ps php /var/www/html/modules/mollie/vendor/bin/phpunit -c /var/www/html/modules/mollie/tests/phpunit.xml -c /var/www/html/modules/mollie/tests/ + run: docker exec temp-ps php /var/www/html/modules/mollie/vendor/bin/phpunit -c /var/www/html/modules/mollie/tests/phpunit.xml # run: docker run --rm --volumes-from temp-ps --workdir=/var/www/html/modules/mollie php ./vendor/bin/phpunit -c ./tests/phpunit.xml -c tests/ phpstan: diff --git a/Makefile b/Makefile index 69c685a5e..3bd83ddb1 100755 --- a/Makefile +++ b/Makefile @@ -72,6 +72,11 @@ build-ps-8: npm-package-install: cd views/assets && npm i && npm run build +run-e2e-tests-locally: + npm install cypress@12.15.0 + npm ci + npx cypress run + # checking the module upgrading - installs older module then installs from master branch upgrading-module-test-1784: git fetch diff --git a/changelog.md b/changelog.md index 4c49cb9d1..8d6670e1f 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,11 @@ # Changelog # +## Changes in release 6.0.1 ## ++ Fixed payment fee tax problems and improved fee set-up process. ++ Sync Mollie components and Single click flag to the environment selected. ++ Overall improvements and bug fixes. + ## Changes in release 6.0.0 ## + Fixed order state duplication on install when single shop instance/ multishop instances were switched during module usage. + Upgraded sentry logger to support PHP 8.0 version. diff --git a/controllers/admin/AdminMollieAjaxController.php b/controllers/admin/AdminMollieAjaxController.php index 158621be7..7c806eab6 100644 --- a/controllers/admin/AdminMollieAjaxController.php +++ b/controllers/admin/AdminMollieAjaxController.php @@ -10,11 +10,14 @@ * @codingStandardsIgnoreStart */ +use Mollie\Adapter\Context; use Mollie\Builder\ApiTestFeedbackBuilder; use Mollie\Config\Config; use Mollie\Provider\CreditCardLogoProvider; +use Mollie\Provider\TaxCalculatorProvider; use Mollie\Repository\PaymentMethodRepository; use Mollie\Service\MolliePaymentMailService; +use Mollie\Utility\NumberUtility; use Mollie\Utility\TimeUtility; class AdminMollieAjaxController extends ModuleAdminController @@ -41,6 +44,9 @@ public function postProcess() case 'validateLogo': $this->validateLogo(); break; + case 'updateFixedPaymentFeePrice': + $this->updateFixedPaymentFeePrice(); + break; default: break; } @@ -147,4 +153,79 @@ private function validateLogo() echo json_encode(['status' => $isUploaded, 'message' => $returnText]); } + + private function updateFixedPaymentFeePrice(): void + { + $paymentFeeTaxIncl = (float) Tools::getValue('paymentFeeTaxIncl'); + $paymentFeeTaxExcl = (float) Tools::getValue('paymentFeeTaxExcl'); + + $taxRulesGroupId = (int) Tools::getValue('taxRulesGroupId'); + + if (empty($paymentFeeTaxIncl) && empty($paymentFeeTaxExcl)) { + $this->ajaxRender( + json_encode([ + 'error' => true, + 'message' => $this->module->l('No fee was submitted.'), + ]) + ); + + return; + } + + if ($paymentFeeTaxIncl < 0.00 || $paymentFeeTaxExcl < 0.00) { + $this->ajaxRender( + json_encode([ + 'error' => true, + 'message' => $this->module->l('Invalid fee'), + ]) + ); + + return; + } + + if ($taxRulesGroupId < 1) { + $this->ajaxRender( + json_encode([ + 'error' => true, + 'message' => $this->module->l('Missing tax rules group ID'), + ]) + ); + + return; + } + + /** @var TaxCalculatorProvider $taxCalculatorProvider */ + $taxCalculatorProvider = $this->module->getService(TaxCalculatorProvider::class); + + /** @var Context $context */ + $context = $this->module->getService(Context::class); + + $taxCalculator = $taxCalculatorProvider->getTaxCalculator( + $taxRulesGroupId, + $context->getCountryId(), + 0 // NOTE: there is no default state for back office so setting no state + ); + + if ($paymentFeeTaxIncl === 0.00) { + $paymentFeeTaxIncl = $taxCalculator->addTaxes($paymentFeeTaxExcl); + } + + if ($paymentFeeTaxExcl === 0.00) { + $paymentFeeTaxExcl = $taxCalculator->removeTaxes($paymentFeeTaxIncl); + } + + $this->ajaxRender( + json_encode([ + 'error' => false, + 'paymentFeeTaxIncl' => NumberUtility::toPrecision( + $paymentFeeTaxIncl, + NumberUtility::FLOAT_PRECISION + ), + 'paymentFeeTaxExcl' => NumberUtility::toPrecision( + $paymentFeeTaxExcl, + NumberUtility::FLOAT_PRECISION + ), + ]) + ); + } } diff --git a/controllers/front/ajax.php b/controllers/front/ajax.php index 0d339e83d..6c6ff8376 100644 --- a/controllers/front/ajax.php +++ b/controllers/front/ajax.php @@ -10,22 +10,28 @@ * @codingStandardsIgnoreStart */ +use Mollie\Adapter\ConfigurationAdapter; +use Mollie\Adapter\ToolsAdapter; +use Mollie\Controller\AbstractMollieController; +use Mollie\Exception\FailedToProvidePaymentFeeException; +use Mollie\Provider\PaymentFeeProviderInterface; +use Mollie\Repository\CurrencyRepositoryInterface; use Mollie\Subscription\Exception\ProductValidationException; use Mollie\Subscription\Exception\SubscriptionProductValidationException; use Mollie\Subscription\Validator\CanProductBeAddedToCartValidator; use Mollie\Utility\NumberUtility; -use PrestaShop\Decimal\DecimalNumber; -class MollieAjaxModuleFrontController extends ModuleFrontController +class MollieAjaxModuleFrontController extends AbstractMollieController { private const FILE_NAME = 'ajax'; /** @var Mollie */ public $module; - public function postProcess() + public function postProcess(): void { $action = Tools::getValue('action'); + switch ($action) { case 'getTotalCartPrice': $this->getTotalCartPrice(); @@ -38,95 +44,143 @@ public function postProcess() } } - private function getTotalCartPrice() + private function getTotalCartPrice(): void { $cart = Context::getContext()->cart; - $paymentFee = Tools::getValue('paymentFee'); - if (!$paymentFee) { - $presentedCart = $this->cart_presenter->present($this->context->cart); - $this->context->smarty->assign([ - 'configuration' => $this->getTemplateVarConfiguration(), - 'cart' => $presentedCart, - 'display_transaction_updated_info' => Tools::getIsset('updatedTransaction'), - ]); - - $this->ajaxDie( - json_encode( - [ - 'cart_summary_totals' => $this->render('checkout/_partials/cart-summary-totals'), - ] - ) - ); + + /** @var ToolsAdapter $tools */ + $tools = $this->module->getService(ToolsAdapter::class); + + $paymentMethodId = (int) $tools->getValue('paymentMethodId'); + + if (!$paymentMethodId) { + $errorData = [ + 'error' => true, + 'message' => 'Failed to get payment method ID.', + ]; + + $this->returnDefaultOrderSummaryBlock($cart, $errorData); + } + + $molPaymentMethod = new MolPaymentMethod($paymentMethodId); + + if (!$molPaymentMethod->id) { + $errorData = [ + 'error' => true, + 'message' => 'Failed to find payment method.', + ]; + + $this->returnDefaultOrderSummaryBlock($cart, $errorData); + } + + /** @var CurrencyRepositoryInterface $currencyRepository */ + $currencyRepository = $this->module->getService(CurrencyRepositoryInterface::class); + + /** @var Currency $cartCurrency */ + $cartCurrency = $currencyRepository->findOneBy([ + 'id_currency' => $cart->id_currency, + ]); + + /** @var PaymentFeeProviderInterface $paymentFeeProvider */ + $paymentFeeProvider = $this->module->getService(PaymentFeeProviderInterface::class); + + /** @var ConfigurationAdapter $configuration */ + $configuration = $this->module->getService(ConfigurationAdapter::class); + + try { + $paymentFeeData = $paymentFeeProvider->getPaymentFee($molPaymentMethod, (float) $cart->getOrderTotal()); + } catch (FailedToProvidePaymentFeeException $exception) { + $errorData = [ + 'error' => true, + 'message' => 'Failed to get payment fee data.', + ]; + + $this->returnDefaultOrderSummaryBlock($cart, $errorData); + + exit; } - $paymentFee = new DecimalNumber(Tools::getValue('paymentFee')); - $orderTotal = new DecimalNumber((string) $cart->getOrderTotal()); - $orderTotalWithFee = NumberUtility::plus($paymentFee->toPrecision(2), $orderTotal->toPrecision(2)); + $orderTotalWithTax = NumberUtility::plus($paymentFeeData->getPaymentFeeTaxIncl(), $cart->getOrderTotal()); - $orderTotalNoTax = new DecimalNumber((string) $cart->getOrderTotal(false)); - $orderTotalNoTaxWithFee = NumberUtility::plus($paymentFee->toPrecision(2), $orderTotalNoTax->toPrecision(2)); + $orderTotalWithoutTax = NumberUtility::plus($paymentFeeData->getPaymentFeeTaxExcl(), $cart->getOrderTotal(false)); - $total_including_tax = $orderTotalWithFee; - $total_excluding_tax = $orderTotalNoTaxWithFee; + $orderTotalTax = NumberUtility::minus($orderTotalWithTax, $orderTotalWithoutTax); $taxConfiguration = new TaxConfiguration(); - $presentedCart = $this->cart_presenter->present($this->context->cart); + $presentedCart = $this->cart_presenter->present($cart); $presentedCart['totals'] = [ 'total' => [ 'type' => 'total', 'label' => $this->module->l('Total', self::FILE_NAME), - 'amount' => $taxConfiguration->includeTaxes() ? $total_including_tax : $total_excluding_tax, - 'value' => Tools::displayPrice( - $taxConfiguration->includeTaxes() ? (float) $total_including_tax : (float) $total_excluding_tax + 'amount' => $taxConfiguration->includeTaxes() ? $orderTotalWithTax : $orderTotalWithoutTax, + 'value' => $this->context->getCurrentLocale()->formatPrice( + $taxConfiguration->includeTaxes() ? $orderTotalWithTax : $orderTotalWithoutTax, + $cartCurrency->iso_code ), ], 'total_including_tax' => [ 'type' => 'total', 'label' => $this->module->l('Total (tax incl.)', self::FILE_NAME), - 'amount' => $total_including_tax, - 'value' => Tools::displayPrice((float) $total_including_tax), + 'amount' => $orderTotalWithTax, + 'value' => $this->context->getCurrentLocale()->formatPrice( + $orderTotalWithTax, + $cartCurrency->iso_code + ), ], 'total_excluding_tax' => [ 'type' => 'total', 'label' => $this->module->l('Total (tax excl.)', self::FILE_NAME), - 'amount' => $total_excluding_tax, - 'value' => Tools::displayPrice((float) $total_excluding_tax), + 'amount' => $orderTotalWithoutTax, + 'value' => $this->context->getCurrentLocale()->formatPrice( + $orderTotalWithoutTax, + $cartCurrency->iso_code + ), ], ]; - $this->context->smarty->assign([ - 'configuration' => $this->getTemplateVarConfiguration(), - 'cart' => $presentedCart, - 'display_transaction_updated_info' => Tools::getIsset('updatedTransaction'), - ]); + if (!$configuration->get('PS_TAX_DISPLAY')) { + $this->returnDefaultOrderSummaryBlock($cart, [], $presentedCart); + } - $this->ajaxDie( - json_encode( - [ - 'cart_summary_totals' => $this->render('checkout/_partials/cart-summary-totals'), - ] - ) - ); + $presentedCart['subtotals'] = [ + 'tax' => [ + 'type' => 'tax', + 'label' => $taxConfiguration->includeTaxes() + ? $this->translator->trans('Included taxes', [], 'Shop.Theme.Checkout') + : $this->translator->trans('Taxes', [], 'Shop.Theme.Checkout'), + 'amount' => $orderTotalTax, + 'value' => $this->context->getCurrentLocale()->formatPrice( + $orderTotalTax, + $cartCurrency->iso_code + ), + ], + ]; + + $this->returnDefaultOrderSummaryBlock($cart, [], $presentedCart); } - private function displayCheckoutError() + private function displayCheckoutError(): void { $errorMessages = explode('#', Tools::getValue('hashTag')); + foreach ($errorMessages as $errorMessage) { if (0 === strpos($errorMessage, 'mollieMessage=')) { $errorMessage = str_replace('mollieMessage=', '', $errorMessage); $errorMessage = str_replace('_', ' ', $errorMessage); + $this->context->smarty->assign([ 'errorMessage' => $errorMessage, ]); - $this->ajaxDie($this->context->smarty->fetch("{$this->module->getLocalPath()}views/templates/front/mollie_error.tpl")); + + $this->ajaxRender($this->context->smarty->fetch("{$this->module->getLocalPath()}views/templates/front/mollie_error.tpl")); } } - $this->ajaxDie(); + + exit; } - private function validateProduct() + private function validateProduct(): void { /** @var CanProductBeAddedToCartValidator $cartValidation */ $cartValidation = $this->module->getService(CanProductBeAddedToCartValidator::class); @@ -135,6 +189,7 @@ private function validateProduct() $productCanBeAdded = true; $message = ''; + try { $cartValidation->validate((int) $product['id_product_attribute']); } catch (ProductValidationException $e) { @@ -145,7 +200,7 @@ private function validateProduct() $message = $this->module->l('Subscription product cannot be added if you have other products in your cart', self::FILE_NAME); } - $this->ajaxDie( + $this->ajaxRender( json_encode( [ 'success' => true, @@ -155,4 +210,30 @@ private function validateProduct() ) ); } + + private function returnDefaultOrderSummaryBlock(Cart $cart, array $errorData = [], array $presentedCart = null): void + { + if (!$presentedCart) { + $presentedCart = $this->cart_presenter->present($cart); + } + + if (empty($errorData)) { + $errorData['error'] = false; + } + + $this->context->smarty->assign(array_merge([ + 'configuration' => $this->getTemplateVarConfiguration(), + 'cart' => $presentedCart, + 'display_transaction_updated_info' => Tools::getIsset('updatedTransaction'), + ], $errorData) + ); + + $this->ajaxRender( + json_encode( + [ + 'cart_summary_totals' => $this->render('checkout/_partials/cart-summary-totals'), + ] + ) + ); + } } diff --git a/controllers/front/payment.php b/controllers/front/payment.php index c26bd8921..042842b53 100644 --- a/controllers/front/payment.php +++ b/controllers/front/payment.php @@ -81,19 +81,17 @@ public function initContent() Tools::redirectLink('index.php'); } - /** @var PaymentMethodRepositoryInterface $paymentMethodRepo */ - $paymentMethodRepo = $this->module->getService(PaymentMethodRepositoryInterface::class); + /** @var PaymentMethodRepositoryInterface $paymentMethodRepository */ + $paymentMethodRepository = $this->module->getService(PaymentMethodRepositoryInterface::class); /** @var PaymentMethodService $transactionService */ $transactionService = $this->module->getService(PaymentMethodService::class); /** @var MollieOrderCreationService $mollieOrderCreationService */ $mollieOrderCreationService = $this->module->getService(MollieOrderCreationService::class); - /** @var PaymentMethodRepositoryInterface $paymentMethodRepository */ - $paymentMethodRepository = $this->module->getService(PaymentMethodRepositoryInterface::class); /** @var SubscriptionOrderValidator $subscriptionOrderValidator */ $subscriptionOrderValidator = $this->module->getService(SubscriptionOrderValidator::class); $environment = (int) Configuration::get(Mollie\Config\Config::MOLLIE_ENVIRONMENT); - $paymentMethodId = $paymentMethodRepo->getPaymentMethodIdByMethodId($method, $environment); + $paymentMethodId = $paymentMethodRepository->getPaymentMethodIdByMethodId($method, $environment); $paymentMethodObj = new MolPaymentMethod((int) $paymentMethodId); /* if its subscription order only payment API is allowed */ @@ -133,7 +131,7 @@ public function initContent() } else { /** @var ExceptionService $exceptionService */ $exceptionService = $this->module->getService(ExceptionService::class); - $message = $exceptionService->getErrorMessageForException($e, $exceptionService->getErrorMessages()); + $message = $exceptionService->getErrorMessageForException($e); } $this->errors[] = $message; @@ -155,7 +153,7 @@ public function initContent() if ($method === PaymentMethod::BANKTRANSFER) { $orderId = Order::getOrderByCartId($cart->id); $order = new Order($orderId); - $paymentMethodRepo->addOpenStatusPayment( + $paymentMethodRepository->addOpenStatusPayment( $cart->id, $apiPayment->method, $apiPayment->id, diff --git a/cypress.config.js b/cypress.config.js index e4fa5aa35..26ec284cf 100755 --- a/cypress.config.js +++ b/cypress.config.js @@ -2,8 +2,9 @@ const { defineConfig } = require('cypress') module.exports = defineConfig({ chromeWebSecurity: false, + experimentalMemoryManagement: true, experimentalSourceRewriting: true, - numTestsKeptInMemory: 5, + numTestsKeptInMemory: 3, defaultCommandTimeout: 15000, projectId: 'xb89dr', retries: 3, @@ -19,6 +20,11 @@ module.exports = defineConfig({ require("cypress-fail-fast/plugin")(on, config); return config; }, + // setupNodeEvents(on, config) { + // require("cypress-fail-fast/plugin")(on, config); + // return config; + // }, + experimentalMemoryManagement: true, excludeSpecPattern: ['index.php'], specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}', }, diff --git a/cypress/e2e/ps1784/02_mollie.ps1784.specs.js b/cypress/e2e/ps1784/02_mollie.ps1784.specs.js index 2e8c3b4da..323d9917c 100755 --- a/cypress/e2e/ps1784/02_mollie.ps1784.specs.js +++ b/cypress/e2e/ps1784/02_mollie.ps1784.specs.js @@ -263,11 +263,11 @@ it('C339352: 15 Klarna Pay Now Checkouting [Orders API]', () => { it('C339353: 16 Klarna Pay Now Order BO Shipping, Refunding [Orders API]', () => { cy.OrderShippingRefundingOrdersAPI() }) -it('C339354: 17 Credit Card Checkouting [Orders API]', () => { +it.only('C339354: 17 Credit Card Checkouting [Orders API]', () => { //Enabling the Single-Click for now cy.visit('/admin1/') cy.OpenModuleDashboard() - cy.get('#MOLLIE_SINGLE_CLICK_PAYMENT_on').click({force:true}) + cy.get('#MOLLIE_SANDBOX_SINGLE_CLICK_PAYMENT_on').click({force:true}) cy.get('[type="submit"]').first().click({force:true}) cy.get('[class="alert alert-success"]').should('be.visible') cy.visit('/SHOP2/en/index.php?controller=history') diff --git a/cypress/e2e/ps1784/03_mollie.ps1784.Subscriptions.WIP.js b/cypress/e2e/ps1784/03_mollie.ps1784.Subscriptions.WIP.js index 1c0a16db0..33999addf 100755 --- a/cypress/e2e/ps1784/03_mollie.ps1784.Subscriptions.WIP.js +++ b/cypress/e2e/ps1784/03_mollie.ps1784.Subscriptions.WIP.js @@ -17,7 +17,7 @@ const login = (MollieBOFOLoggingIn) => { }) } -//Checing the console for errors +//Checking the console for errors let windowConsoleError; Cypress.on('window:before:load', (win) => { windowConsoleError = cy.spy(win.console, 'error'); @@ -30,31 +30,32 @@ describe('PS1784 Subscriptions Test Suit', () => { cy.viewport(1920,1080) login('MollieBOFOLoggingIn') }) -it.skip('C176305 Check if Subscription options added in Product BO', () => { +it('C176305 Check if Subscription options added in Product BO', () => { cy.visit('/admin1/') cy.get('#subtab-AdminCatalog > :nth-child(1)').click() cy.get('#subtab-AdminProducts > .link').click() - cy.contains('Hummingbird printed t-shirt').click() - cy.get('[name="show_variations"]').check() + cy.get('[data-product-id="8"]').find('[class="btn tooltip-link product-edit"]').click() + cy.contains('Product with combinations').click() cy.get('[id="tab_step3"]').click() cy.contains('Daily').click({force:true}) cy.get('[class="token"]').should('be.visible') cy.get('#create-combinations').click() cy.wait(5000) - cy.contains('Mollie subscription - Daily').should('be.visible') + cy.reload() cy.wait(5000) + cy.contains('Mollie Subscription - Daily').should('be.visible') cy.get('[class="attribute-quantity"]').last().find('[type="text"]').clear().type('999') cy.get('#submit').click() cy.get('.growl-message').contains('Settings updated.') //Check if Subscription options are in Product Page FO cy.visit('/SHOP2/de/') - cy.get('.products > :nth-child(1)').click() + cy.get('[data-id-product="8"]').click() //possible PS1784 notice exception, checking with dev... cy.get('a').click() //wip ... //Check if Subscription options are implemented in My Account FO cy.visit('/SHOP2/') cy.get('[class="account"]').click() - cy.contains('Mollie subscriptions').click() + cy.contains('Subscriptions').click() cy.get('[class="page-content"]').should('be.visible') //wip ... }); diff --git a/cypress/e2e/ps8/02_mollie.ps8.specs.js b/cypress/e2e/ps8/02_mollie.ps8.specs.js index 5eaf42eb6..7e285d7a5 100755 --- a/cypress/e2e/ps8/02_mollie.ps8.specs.js +++ b/cypress/e2e/ps8/02_mollie.ps8.specs.js @@ -255,7 +255,7 @@ it('C339354: 17 Credit Card Checkouting [Orders API]', () => { cy.visit('/admin1/') cy.get('#subtab-AdminMollieModule_MTR > :nth-child(1)').click() cy.get('#subtab-AdminMollieModule > .link').click() - cy.get('#MOLLIE_SINGLE_CLICK_PAYMENT_on').click({force:true}) + cy.get('#MOLLIE_SANDBOX_SINGLE_CLICK_PAYMENT_on').click({force:true}) cy.get('[type="submit"]').first().click({force:true}) cy.get('[class="alert alert-success"]').should('be.visible') cy.visit('/SHOP2/en/index.php?controller=history') diff --git a/cypress/e2e/ps8/03_mollie.ps8.Subscriptions.WIP.js b/cypress/e2e/ps8/03_mollie.ps8.Subscriptions.WIP.js new file mode 100644 index 000000000..efa705bb8 --- /dev/null +++ b/cypress/e2e/ps8/03_mollie.ps8.Subscriptions.WIP.js @@ -0,0 +1,61 @@ +//Caching the BO and FO session +const login = (MollieBOFOLoggingIn) => { + cy.session(MollieBOFOLoggingIn,() => { + cy.visit('/admin1/') + cy.url().should('contain', 'https').as('Check if HTTPS exists') + cy.get('#email').type('demo@prestashop.com',{delay: 0, log: false}) + cy.get('#passwd').type('prestashop_demo',{delay: 0, log: false}) + cy.get('#submit_login').click().wait(1000).as('Connection successsful') + //switching the multistore PS1784 + cy.get('#header_shop > .dropdown').click() + cy.get('.open > .dropdown-menu').find('[class="shop"]').eq(1).find('[href]').eq(0).click() + cy.visit('/SHOP2/index.php?controller=my-account') + cy.get('#login-form [name="email"]').eq(0).type('demo@demo.com') + cy.get('#login-form [name="password"]').eq(0).type('prestashop_demo') + cy.get('#login-form [type="submit"]').eq(0).click({force:true}) + cy.get('#history-link > .link-item').click() + }) + } + +//Checking the console for errors +let windowConsoleError; +Cypress.on('window:before:load', (win) => { +windowConsoleError = cy.spy(win.console, 'error'); +}) +afterEach(() => { +expect(windowConsoleError).to.not.be.called; +}) +describe('PS8 Subscriptions Test Suit', () => { + beforeEach(() => { + cy.viewport(1920,1080) + login('MollieBOFOLoggingIn') + }) +it('C176305 Check if Subscription options added in Product BO', () => { + cy.visit('/admin1/') + cy.get('#subtab-AdminCatalog > :nth-child(1)').click() + cy.get('#subtab-AdminProducts > .link').click() + cy.contains('Hummingbird printed t-shirt').click() + cy.contains('Product with combinations').click() + cy.get('[id="tab_step3"]').click() + cy.contains('Daily').click({force:true}) + cy.get('[class="token"]').should('be.visible') + cy.get('#create-combinations').click() + cy.reload() + cy.wait(5000) + cy.contains('Mollie Subscription - Daily').should('be.visible') + cy.get('[class="attribute-quantity"]').last().find('[type="text"]').clear().type('999') + cy.get('#submit').click() + cy.get('.growl-message').contains('Settings updated.') //somehow PS8 has a combinations creation bug probably... + //Check if Subscription options are in Product Page FO + cy.visit('/SHOP2/de/') + // cy.get('.products > :nth-child(1)').click() + // cy.get('a').click() + // wip ... + //Check if Subscription options are implemented in My Account FO + // cy.visit('/SHOP2/') + // cy.get('[class="account"]').click() + // cy.contains('Mollie subscriptions').click() + // cy.get('[class="page-content"]').should('be.visible') + // wip ... +}); +}) diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js index 4e3c1954d..e092d706b 100644 --- a/cypress/plugins/index.js +++ b/cypress/plugins/index.js @@ -25,6 +25,6 @@ module.exports = (on, config) => { on('before:browser:launch', (browser = {}, const TestRailReporter = require('cypress-testrail'); module.exports = (on, config) => { -new TestRailReporter(on, config).register(); -return config + new TestRailReporter(on, config).register(); + return config } diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 7f4653142..8d447c062 100755 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -41,20 +41,20 @@ import 'cypress-iframe'; // -- This is a parent command -- // Cypress.Commands.add("login", (email, password) => { ... }) Cypress.Commands.add("ConfOrdersAPI", () => { - cy.get('[for="MOLLIE_IFRAME_on"]').click() cy.get('#MOLLIE_PROFILE_ID').clear({force: true}).type((Cypress.env('MOLLIE_TEST_PROFILE_ID')),{delay: 0, log: false}) //giropay cy.get('[name="MOLLIE_METHOD_ENABLED_giropay"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_giropay"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_giropay"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_giropay"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_giropay"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_giropay"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_giropay"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_giropay"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_giropay"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_giropay"]').clear({force: true}).type('22', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_giropay"]').clear({force: true}).type('33', {force: true}) //eps cy.get('[name="MOLLIE_METHOD_ENABLED_eps"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_eps"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_eps"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_eps"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_eps"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_eps"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_eps"]').clear({force: true}).type('22', {force: true}) @@ -62,7 +62,7 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //przelewy24 cy.get('[name="MOLLIE_METHOD_ENABLED_przelewy24"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_przelewy24"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_przelewy24"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_przelewy24"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_przelewy24"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_przelewy24"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_przelewy24"]').clear({force: true}).type('22', {force: true}) @@ -70,7 +70,7 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //kbc cy.get('[name="MOLLIE_METHOD_ENABLED_kbc"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_kbc"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_kbc"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_kbc"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_kbc"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_kbc"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_kbc"]').clear({force: true}).type('22', {force: true}) @@ -78,7 +78,7 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //voucher cy.get('[name="MOLLIE_METHOD_ENABLED_voucher"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_voucher"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_voucher"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_voucher"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_voucher"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_voucher"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_voucher"]').clear({force: true}).type('22', {force: true}) @@ -86,7 +86,7 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //belfius cy.get('[name="MOLLIE_METHOD_ENABLED_belfius"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_belfius"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_przelewy24"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_przelewy24"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_belfius"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_belfius"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_belfius"]').clear({force: true}).type('22', {force: true}) @@ -94,7 +94,7 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //bancontact cy.get('[name="MOLLIE_METHOD_ENABLED_bancontact"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_bancontact"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_bancontact"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_bancontact"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_bancontact"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_bancontact"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_bancontact"]').clear({force: true}).type('22', {force: true}) @@ -102,7 +102,7 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //sofort cy.get('[name="MOLLIE_METHOD_ENABLED_sofort"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_sofort"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_sofort"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_sofort"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_sofort"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_sofort"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_sofort"]').clear({force: true}).type('22', {force: true}) @@ -110,7 +110,7 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //creditcard cy.get('[name="MOLLIE_METHOD_ENABLED_creditcard"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_creditcard"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_creditcard"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_creditcard"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_creditcard"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_creditcard"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_creditcard"]').clear({force: true}).type('22', {force: true}) @@ -118,28 +118,28 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //ideal cy.get('[name="MOLLIE_METHOD_ENABLED_ideal"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_ideal"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_ideal"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_ideal"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_ideal"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_ideal"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_ideal"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_ideal"]').clear({force: true}).type('33', {force: true}) //klarnapaylater cy.get('[name="MOLLIE_METHOD_ENABLED_klarnapaylater"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaylater"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaylater"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_klarnapaylater"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_klarnapaylater"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_klarnapaylater"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_klarnapaylater"]').clear({force: true}).type('33', {force: true}) //klarnasliceit cy.get('[name="MOLLIE_METHOD_ENABLED_klarnasliceit"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnasliceit"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnasliceit"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_klarnasliceit"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_klarnasliceit"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_klarnasliceit"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_klarnasliceit"]').clear({force: true}).type('33', {force: true}) //klarnapaynow cy.get('[name="MOLLIE_METHOD_ENABLED_klarnapaynow"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaynow"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaynow"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_klarnapaynow"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_klarnapaynow"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_klarnapaynow"]').clear({force: true}).type('22', {force: true}) @@ -147,7 +147,7 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //banktransfer cy.get('[name="MOLLIE_METHOD_ENABLED_banktransfer"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_banktransfer"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_banktransfer"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_banktransfer"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_banktransfer"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_banktransfer"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_banktransfer"]').clear({force: true}).type('22', {force: true}) @@ -155,14 +155,14 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //paypal cy.get('[name="MOLLIE_METHOD_ENABLED_paypal"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_paypal"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_paypal"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_paypal"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_paypal"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_paypal"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_paypal"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_paypal"]').clear({force: true}).type('33', {force: true}) //applepay cy.get('[name="MOLLIE_METHOD_ENABLED_applepay"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_applepay"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_applepay"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_applepay"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_applepay"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_applepay"]').clear({force: true}).type('22', {force: true}) @@ -170,7 +170,7 @@ Cypress.Commands.add("ConfOrdersAPI", () => { //in3 cy.get('[name="MOLLIE_METHOD_ENABLED_in3"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_in3"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_in3"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_in3"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_in3"]').select('3', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_in3"]').clear({force: true}).type('11', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_in3"]').clear({force: true}).type('22', {force: true}) @@ -247,143 +247,176 @@ Cypress.Commands.add("ConfPaymentsAPI", () => { cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_applepay"]').clear({force: true}).type('33', {force: true}) }) Cypress.Commands.add("ConfOrdersAPI1784", () => { - cy.get('#MOLLIE_IFRAME_on').click({force:true}) //giropay cy.get('[name="MOLLIE_METHOD_ENABLED_giropay"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_giropay"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_giropay"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_giropay"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_giropay"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_giropay"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_giropay"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_giropay"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_giropay"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_giropay"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_giropay"]').clear({force: true}).type('33', {force: true}) //eps cy.get('[name="MOLLIE_METHOD_ENABLED_eps"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_eps"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_eps"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_eps"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_eps"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_eps"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_eps"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_eps"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_eps"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_eps"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_eps"]').clear({force: true}).type('33', {force: true}) //przelewy24 cy.get('[name="MOLLIE_METHOD_ENABLED_przelewy24"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_przelewy24"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_przelewy24"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_przelewy24"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_przelewy24"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_przelewy24"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_przelewy24"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_przelewy24"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_przelewy24"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_przelewy24"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_przelewy24"]').clear({force: true}).type('33', {force: true}) //kbc cy.get('[name="MOLLIE_METHOD_ENABLED_kbc"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_kbc"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_kbc"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_kbc"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_kbc"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_kbc"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_kbc"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_kbc"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_kbc"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_kbc"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_kbc"]').clear({force: true}).type('33', {force: true}) //voucher cy.get('[name="MOLLIE_METHOD_ENABLED_voucher"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_voucher"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_voucher"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_voucher"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_voucher"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_voucher"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_voucher"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_voucher"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_voucher"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_voucher"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_voucher"]').clear({force: true}).type('33', {force: true}) //belfius cy.get('[name="MOLLIE_METHOD_ENABLED_belfius"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_belfius"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_przelewy24"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_belfius"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_belfius"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_belfius"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_belfius"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_belfius"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_belfius"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_belfius"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_belfius"]').clear({force: true}).type('33', {force: true}) //bancontact cy.get('[name="MOLLIE_METHOD_ENABLED_bancontact"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_bancontact"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_bancontact"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_bancontact"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_bancontact"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_bancontact"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_bancontact"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_bancontact"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_bancontact"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_bancontact"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_bancontact"]').clear({force: true}).type('33', {force: true}) //sofort cy.get('[name="MOLLIE_METHOD_ENABLED_sofort"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_sofort"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_sofort"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_sofort"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_sofort"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_sofort"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_sofort"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_sofort"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_sofort"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_sofort"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_sofort"]').clear({force: true}).type('33', {force: true}) //creditcard cy.get('[name="MOLLIE_METHOD_ENABLED_creditcard"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_creditcard"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_creditcard"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_creditcard"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_creditcard"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_creditcard"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_creditcard"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_creditcard"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_creditcard"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_creditcard"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_creditcard"]').clear({force: true}).type('33', {force: true}) //ideal cy.get('[name="MOLLIE_METHOD_ENABLED_ideal"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_ideal"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_ideal"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_ideal"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_ideal"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_ideal"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_ideal"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_ideal"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_ideal"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_ideal"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_ideal"]').clear({force: true}).type('33', {force: true}) //klarnapaylater cy.get('[name="MOLLIE_METHOD_ENABLED_klarnapaylater"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaylater"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaylater"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_klarnapaylater"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_klarnapaylater"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_klarnapaylater"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_klarnapaylater"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_klarnapaylater"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_klarnapaylater"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_klarnapaylater"]').clear({force: true}).type('33', {force: true}) //klarnasliceit cy.get('[name="MOLLIE_METHOD_ENABLED_klarnasliceit"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnasliceit"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnasliceit"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_klarnasliceit"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_klarnasliceit"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_klarnasliceit"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_klarnasliceit"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_klarnasliceit"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_klarnasliceit"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_klarnasliceit"]').clear({force: true}).type('33', {force: true}) //klarnapaynow cy.get('[name="MOLLIE_METHOD_ENABLED_klarnapaynow"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaynow"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaynow"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_klarnapaynow"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_klarnapaynow"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_klarnapaynow"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_klarnapaynow"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_klarnapaynow"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_klarnapaynow"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_klarnapaynow"]').clear({force: true}).type('33', {force: true}) //banktransfer cy.get('[name="MOLLIE_METHOD_ENABLED_banktransfer"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_banktransfer"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_banktransfer"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_banktransfer"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_banktransfer"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_banktransfer"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_banktransfer"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_banktransfer"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_banktransfer"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_banktransfer"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_banktransfer"]').clear({force: true}).type('33', {force: true}) //paypal cy.get('[name="MOLLIE_METHOD_ENABLED_paypal"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_paypal"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_paypal"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_paypal"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_paypal"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_paypal"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_paypal"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_paypal"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_paypal"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_paypal"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_paypal"]').clear({force: true}).type('33', {force: true}) //applepay cy.get('[name="MOLLIE_METHOD_ENABLED_applepay"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_applepay"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_applepay"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_applepay"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_applepay"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_applepay"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_applepay"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_applepay"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_applepay"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_applepay"]').clear({force: true}).type('33', {force: true}) //in3 cy.get('[name="MOLLIE_METHOD_ENABLED_in3"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_in3"]').select('Orders API', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_in3"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + cy.get('[name="MOLLIE_METHOD_DESCRIPTION_in3"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_in3"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_in3"]').clear({force: true}).type('11', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_in3"]').clear({force: true}).type('4', {force: true}) + cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_in3"]').clear({force: true}).type('5', {force: true}) + cy.get('[name="MOLLIE_METHOD_TAX_RULES_GROUP_ID_in3"]').select('1', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_in3"]').clear({force: true}).type('22', {force: true}) cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_in3"]').clear({force: true}).type('33', {force: true}) //Gift card // cy.get('[name="MOLLIE_METHOD_ENABLED_giftcard"]').select('Yes', {force: true}) // cy.get('[name="MOLLIE_METHOD_API_giftcard"]').select('Orders API', {force: true}) - // cy.get('[name="MOLLIE_METHOD_DESCRIPTION_giftcard"]').clear({force: true}).type('Lorem Ipsum dummy text 123 !@#$%^&*', {force: true}) + // cy.get('[name="MOLLIE_METHOD_DESCRIPTION_giftcard"]').clear({force: true}).type('text 123 !@#$%^&*', {force: true}) // cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_giftcard"]').select('3', {force: true}) // cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_giftcard"]').clear({force: true}).type('11', {force: true}) // cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_giftcard"]').clear({force: true}).type('22', {force: true}) @@ -405,13 +438,6 @@ Cypress.Commands.add("ConfPaymentsAPI1784", () => { //kbc cy.get('[name="MOLLIE_METHOD_ENABLED_kbc"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_kbc"]').select('Payments API', {force: true}) - //voucher - cy.get('[name="MOLLIE_METHOD_ENABLED_voucher"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_voucher"]').clear({force: true}).type('Lorem Ipsum 123 !@#$%^&*', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_voucher"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_voucher"]').clear({force: true}).type('11', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_voucher"]').clear({force: true}).type('22', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_voucher"]').clear({force: true}).type('33', {force: true}) //belfius cy.get('[name="MOLLIE_METHOD_ENABLED_belfius"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_belfius"]').select('Payments API', {force: true}) @@ -427,27 +453,6 @@ Cypress.Commands.add("ConfPaymentsAPI1784", () => { //ideal cy.get('[name="MOLLIE_METHOD_ENABLED_ideal"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_ideal"]').select('Payments API', {force: true}) - //klarnapaylater - cy.get('[name="MOLLIE_METHOD_ENABLED_klarnapaylater"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaylater"]').clear({force: true}).type('Lorem Ipsum 123 !@#$%^&*', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_klarnapaylater"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_klarnapaylater"]').clear({force: true}).type('11', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_klarnapaylater"]').clear({force: true}).type('22', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_klarnapaylater"]').clear({force: true}).type('33', {force: true}) - //klarnasliceit - cy.get('[name="MOLLIE_METHOD_ENABLED_klarnasliceit"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnasliceit"]').clear({force: true}).type('Lorem Ipsum 123 !@#$%^&*', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_klarnasliceit"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_klarnasliceit"]').clear({force: true}).type('11', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_klarnasliceit"]').clear({force: true}).type('22', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_klarnasliceit"]').clear({force: true}).type('33', {force: true}) - //klarnapaynow - cy.get('[name="MOLLIE_METHOD_ENABLED_klarnapaynow"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_klarnapaynow"]').clear({force: true}).type('Lorem Ipsum 123 !@#$%^&*', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_klarnapaynow"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_klarnapaynow"]').clear({force: true}).type('11', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_klarnapaynow"]').clear({force: true}).type('22', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_klarnapaynow"]').clear({force: true}).type('33', {force: true}) //banktransfer cy.get('[name="MOLLIE_METHOD_ENABLED_banktransfer"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_banktransfer"]').select('Payments API', {force: true}) @@ -455,12 +460,7 @@ Cypress.Commands.add("ConfPaymentsAPI1784", () => { cy.get('[name="MOLLIE_METHOD_ENABLED_paypal"]').select('Yes', {force: true}) cy.get('[name="MOLLIE_METHOD_API_paypal"]').select('Payments API', {force: true}) //applepay - cy.get('[name="MOLLIE_METHOD_ENABLED_applepay"]').select('Yes', {force: true}) - cy.get('[name="MOLLIE_METHOD_DESCRIPTION_applepay"]').clear({force: true}).type('Lorem Ipsum 123 !@#$%^&*', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_TYPE_applepay"]').select('3', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_applepay"]').clear({force: true}).type('11', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_PERCENTAGE_applepay"]').clear({force: true}).type('22', {force: true}) - cy.get('[name="MOLLIE_METHOD_SURCHARGE_LIMIT_applepay"]').clear({force: true}).type('33', {force: true}) + cy.get('[name="MOLLIE_METHOD_API_applepay"]').select('Payments API', {force: true}) }) Cypress.Commands.add("login_mollie17_test", () => { Cypress.env() diff --git a/mollie.php b/mollie.php index 4fd39e39e..0262f6965 100755 --- a/mollie.php +++ b/mollie.php @@ -14,10 +14,16 @@ use Mollie\Adapter\Link; use Mollie\Adapter\ProductAttributeAdapter; use Mollie\Adapter\ToolsAdapter; +use Mollie\Api\Exceptions\ApiException; use Mollie\Config\Config; +use Mollie\Exception\ShipmentCannotBeSentException; use Mollie\Handler\ErrorHandler\ErrorHandler; +use Mollie\Handler\Shipment\ShipmentSenderHandlerInterface; +use Mollie\Logger\PrestaLoggerInterface; use Mollie\Provider\ProfileIdProviderInterface; +use Mollie\Repository\MolOrderPaymentFeeRepositoryInterface; use Mollie\Repository\PaymentMethodRepositoryInterface; +use Mollie\Service\ExceptionService; use Mollie\ServiceProvider\LeagueServiceContainerProvider; use Mollie\Subscription\Exception\ProductValidationException; use Mollie\Subscription\Exception\SubscriptionProductValidationException; @@ -32,6 +38,7 @@ use Mollie\Subscription\Validator\CanProductBeAddedToCartValidator; use Mollie\Subscription\Verification\HasSubscriptionProductInCart; use Mollie\Utility\PsVersionUtility; +use Mollie\Verification\IsPaymentInformationAvailable; use Symfony\Component\Dotenv\Dotenv; use Symfony\Component\HttpFoundation\Response; @@ -73,7 +80,7 @@ public function __construct() { $this->name = 'mollie'; $this->tab = 'payments_gateways'; - $this->version = '6.0.0'; + $this->version = '6.0.1'; $this->author = 'Mollie B.V.'; $this->need_instance = 1; $this->bootstrap = true; @@ -158,20 +165,10 @@ public function install() } // TODO inject base install and subscription services - $installer = new \Mollie\Install\Installer( - $this, - new \Mollie\Service\OrderStateImageService(), - new \Mollie\Install\DatabaseTableInstaller(), - new \Mollie\Tracker\Segment( - new \Mollie\Adapter\Shop(), - new \Mollie\Adapter\Language(), - new \Mollie\Config\Env() - ), - new \Mollie\Adapter\ConfigurationAdapter() - ); + $coreInstaller = $this->getService(Mollie\Install\Installer::class); - if (!$installer->install()) { - $this->_errors = array_merge($this->_errors, $installer->getErrors()); + if (!$coreInstaller->install()) { + $this->_errors = array_merge($this->_errors, $coreInstaller->getErrors()); return false; } @@ -410,6 +407,12 @@ public function hookActionAdminControllerSetMedia() // We are on module configuration page if ('AdminMollieSettings' === $currentController) { + Media::addJsDef([ + 'paymentMethodTaxRulesGroupIdConfig' => Config::MOLLIE_METHOD_TAX_RULES_GROUP_ID, + 'paymentMethodSurchargeFixedAmountTaxInclConfig' => Config::MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL, + 'paymentMethodSurchargeFixedAmountTaxExclConfig' => Config::MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL, + ]); + $this->context->controller->addJqueryPlugin('sortable'); $this->context->controller->addJS($this->getPathUri() . 'views/js/admin/payment_methods.js'); $this->context->controller->addCSS($this->getPathUri() . 'views/css/admin/payment_methods.css'); @@ -499,6 +502,7 @@ public function hookPaymentOptions($params) if (version_compare(_PS_VERSION_, '1.7.0.0', '<')) { return []; } + $paymentOptions = []; /** @var PaymentMethodRepositoryInterface $paymentMethodRepository */ @@ -510,6 +514,9 @@ public function hookPaymentOptions($params) /** @var \Mollie\Service\PaymentMethodService $paymentMethodService */ $paymentMethodService = $this->getService(\Mollie\Service\PaymentMethodService::class); + /** @var PrestaLoggerInterface $logger */ + $logger = $this->getService(PrestaLoggerInterface::class); + $methods = $paymentMethodService->getMethodsForCheckout(); foreach ($methods as $method) { @@ -519,8 +526,16 @@ public function hookPaymentOptions($params) if (!$paymentMethod) { continue; } + $paymentMethod->method_name = $method['method_name']; - $paymentOptions[] = $paymentOptionsHandler->handle($paymentMethod); + + try { + $paymentOptions[] = $paymentOptionsHandler->handle($paymentMethod); + } catch (Exception $exception) { + // TODO handle payment fee exception and other exceptions with custom exception throw + + $logger->error($exception->getMessage()); + } } return $paymentOptions; @@ -578,9 +593,9 @@ public function displayAjaxMollieOrderInfo() * * @since 3.3.0 */ - public function hookActionOrderStatusUpdate($params = []) + public function hookActionOrderStatusUpdate(array $params): void { - if (!isset($params['newOrderStatus']) || !isset($params['id_order'])) { + if (!isset($params['newOrderStatus'], $params['id_order'])) { return; } @@ -589,6 +604,7 @@ public function hookActionOrderStatusUpdate($params = []) } else { $orderStatus = new OrderState((int) $params['newOrderStatus']); } + $order = new Order($params['id_order']); if (!Validate::isLoadedObject($orderStatus)) { @@ -599,32 +615,40 @@ public function hookActionOrderStatusUpdate($params = []) return; } - $idOrder = $params['id_order']; - $order = new Order($idOrder); - $checkStatuses = []; - if (Configuration::get(Mollie\Config\Config::MOLLIE_AUTO_SHIP_STATUSES)) { - $checkStatuses = @json_decode(Configuration::get(Mollie\Config\Config::MOLLIE_AUTO_SHIP_STATUSES)); - } - if (!is_array($checkStatuses)) { - $checkStatuses = []; - } - if (!(Configuration::get(Mollie\Config\Config::MOLLIE_AUTO_SHIP_MAIN) && in_array($orderStatus->id, $checkStatuses)) - ) { + if (!$this->getApiClient()) { return; } - /** @var \Mollie\Handler\Shipment\ShipmentSenderHandlerInterface $shipmentSenderHandler */ - $shipmentSenderHandler = $this->getService( - Mollie\Handler\Shipment\ShipmentSenderHandlerInterface::class - ); + /** @var IsPaymentInformationAvailable $isPaymentInformationAvailable */ + $isPaymentInformationAvailable = $this->getService(IsPaymentInformationAvailable::class); - if (!$this->getApiClient()) { + if (!$isPaymentInformationAvailable->verify((int) $order->id)) { return; } + + /** @var ShipmentSenderHandlerInterface $shipmentSenderHandler */ + $shipmentSenderHandler = $this->getService(ShipmentSenderHandlerInterface::class); + + /** @var ExceptionService $exceptionService */ + $exceptionService = $this->getService(ExceptionService::class); + + /** @var PrestaLoggerInterface $logger */ + $logger = $this->getService(PrestaLoggerInterface::class); + try { $shipmentSenderHandler->handleShipmentSender($this->getApiClient(), $order, $orderStatus); - } catch (Exception $e) { - //todo: we logg error in handleShipment + } catch (ShipmentCannotBeSentException $exception) { + $logger->error($exceptionService->getErrorMessageForException( + $exception, + [], + ['orderReference' => $order->reference] + )); + + return; + } catch (ApiException $exception) { + $logger->error($exception->getMessage()); + + return; } } @@ -642,9 +666,15 @@ public function hookActionEmailSendBefore($params) $cart = new Cart($params['cart']->id); $orderId = Order::getOrderByCartId($cart->id); $order = new Order($orderId); + + if (!Validate::isLoadedObject($order)) { + return true; + } + if ($order->module !== $this->name) { return true; } + /** @var \Mollie\Validator\OrderConfMailValidator $orderConfMailValidator */ $orderConfMailValidator = $this->getService(\Mollie\Validator\OrderConfMailValidator::class); @@ -669,26 +699,29 @@ public function hookActionEmailSendBefore($params) 'outofstock' === $template || 'bankwire' === $template || 'refund' === $template) { - $orderId = Order::getOrderByCartId($cart->id); - $order = new Order($orderId); - if (!Validate::isLoadedObject($order)) { - return true; - } - try { - /** @var \Mollie\Repository\OrderFeeRepository $orderFeeRepo */ - $orderFeeRepo = $this->getService(\Mollie\Repository\OrderFeeRepository::class); - $orderFeeId = $orderFeeRepo->getOrderFeeIdByCartId($cart->id); - $orderFee = new MolOrderFee($orderFeeId); - } catch (Exception $e) { - PrestaShopLogger::addLog(__METHOD__ . ' said: ' . $e->getMessage(), Mollie\Config\Config::CRASH); - - return true; - } - if ($orderFee->order_fee) { - $params['templateVars']['{payment_fee}'] = Tools::displayPrice($orderFee->order_fee); + /** @var MolOrderPaymentFeeRepositoryInterface $molOrderPaymentFeeRepository */ + $molOrderPaymentFeeRepository = $this->getService(MolOrderPaymentFeeRepositoryInterface::class); + + $orderCurrency = new Currency($order->id_currency); + + /** @var MolOrderPaymentFee|null $molOrderPaymentFee */ + $molOrderPaymentFee = $molOrderPaymentFeeRepository->findOneBy([ + 'id_order' => (int) $order->id, + ]); + + if (!$molOrderPaymentFee) { + $orderFee = $this->context->getCurrentLocale()->formatPrice( + 0, + $orderCurrency->iso_code + ); } else { - $params['templateVars']['{payment_fee}'] = Tools::displayPrice(0); + $orderFee = $this->context->getCurrentLocale()->formatPrice( + $molOrderPaymentFee->fee_tax_incl, + $orderCurrency->iso_code + ); } + + $params['templateVars']['{payment_fee}'] = $orderFee; } if ('order_conf' === $template) { @@ -698,24 +731,37 @@ public function hookActionEmailSendBefore($params) return true; } - public function hookDisplayPDFInvoice($params) + public function hookDisplayPDFInvoice($params): string { if (!isset($params['object'])) { - return; + return ''; } + if (!$params['object'] instanceof OrderInvoice) { - return; + return ''; } + $localeRepo = $this->get('prestashop.core.localization.locale.repository'); + + if (!$localeRepo) { + return ''; + } + + /** + * NOTE: context language is set based on customer/employee context + */ + $locale = $localeRepo->getLocale($this->context->language->getLocale()); + /** @var \Mollie\Builder\InvoicePdfTemplateBuilder $invoiceTemplateBuilder */ $invoiceTemplateBuilder = $this->getService(\Mollie\Builder\InvoicePdfTemplateBuilder::class); $templateParams = $invoiceTemplateBuilder ->setOrder($params['object']->getOrder()) + ->setLocale($locale) ->buildParams(); if (empty($templateParams)) { - return; + return ''; } $this->context->smarty->assign($templateParams); @@ -1053,6 +1099,7 @@ private function setApiKey($shopId = null) return; } try { + // TODO handle api key set differently. Throw error and don't let do further actions. $this->api = $apiKeyService->setApiKey($apiKey, $this->version); } catch (\Mollie\Api\Exceptions\IncompatiblePlatform $e) { $errorHandler = \Mollie\Handler\ErrorHandler\ErrorHandler::getInstance(); diff --git a/package-lock.json b/package-lock.json index 8d300342c..28b82889a 100755 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "cypress": "^12.14.0", + "cypress": "^12.15.0", "cypress-fail-fast": "^7.0.0", "cypress-iframe": "^1.0.1", "cypress-testrail": "^2.7.1", @@ -692,9 +692,9 @@ } }, "node_modules/cypress": { - "version": "12.14.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.14.0.tgz", - "integrity": "sha512-HiLIXKXZaIT1RT7sw1sVPt+qKtis3uYNm6KwC4qoYjabwLKaqZlyS/P+uVvvlBNcHIwL/BC6nQZajpbUd7hOgQ==", + "version": "12.15.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.15.0.tgz", + "integrity": "sha512-FqGbxsH+QgjStuTO9onXMIeF44eOrgVwPvlcvuzLIaePQMkl72YgBvpuHlBGRcrw3Q4SvqKfajN8iV5XWShAiQ==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -2598,9 +2598,9 @@ } }, "cypress": { - "version": "12.14.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.14.0.tgz", - "integrity": "sha512-HiLIXKXZaIT1RT7sw1sVPt+qKtis3uYNm6KwC4qoYjabwLKaqZlyS/P+uVvvlBNcHIwL/BC6nQZajpbUd7hOgQ==", + "version": "12.15.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.15.0.tgz", + "integrity": "sha512-FqGbxsH+QgjStuTO9onXMIeF44eOrgVwPvlcvuzLIaePQMkl72YgBvpuHlBGRcrw3Q4SvqKfajN8iV5XWShAiQ==", "dev": true, "requires": { "@cypress/request": "^2.88.10", diff --git a/package.json b/package.json index 679e5b90a..fc3ecf856 100755 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "homepage": "https://github.com/mollie/PrestaShop#readme", "devDependencies": { - "cypress": "^12.14.0", + "cypress": "^12.15.0", "cypress-fail-fast": "^7.0.0", "cypress-iframe": "^1.0.1", "cypress-testrail": "^2.7.1", diff --git a/src/Adapter/CartAdapter.php b/src/Adapter/CartAdapter.php index 86a173ed6..3ff5c21da 100644 --- a/src/Adapter/CartAdapter.php +++ b/src/Adapter/CartAdapter.php @@ -16,6 +16,7 @@ public function getCart(): Cart public function getProducts(): array { - return Context::getContext()->cart->getProducts(); + /* @phpstan-ignore-next-line */ + return Context::getContext()->cart ? Context::getContext()->cart->getProducts() : []; } } diff --git a/src/Adapter/ConfigurationAdapter.php b/src/Adapter/ConfigurationAdapter.php index f48b80f84..38ddb4d5a 100644 --- a/src/Adapter/ConfigurationAdapter.php +++ b/src/Adapter/ConfigurationAdapter.php @@ -13,12 +13,21 @@ namespace Mollie\Adapter; use Context; +use Mollie\Config\Config; use Shop; class ConfigurationAdapter { public function get($key, $idShop = null, $idLang = null, $idShopGroup = null) { + if (is_array($key)) { + if ((int) $this->get(Config::MOLLIE_ENVIRONMENT)) { + $key = $key['production']; + } else { + $key = $key['sandbox']; + } + } + if (!$idShop) { $idShop = Context::getContext()->shop->id; } @@ -30,17 +39,50 @@ public function get($key, $idShop = null, $idLang = null, $idShopGroup = null) return \Configuration::get($key, $idLang, $idShopGroup, $idShop); } - public function updateValue($key, $values, $idShop = null, $html = false, $idShopGroup = null) + /** + * @param string|array{production: string, sandbox: string} $key + * @param mixed $value + * @param ?int $idShop + * @param bool $html + * @param ?int $idShopGroup + * + * @return void + */ + public function updateValue($key, $value, $idShop = null, $html = false, $idShopGroup = null) { + if (is_array($key)) { + if ((int) $this->get(Config::MOLLIE_ENVIRONMENT)) { + $key = $key['production']; + } else { + $key = $key['sandbox']; + } + } + if ($idShop === null) { $shops = Shop::getShops(true); foreach ($shops as $shop) { - \Configuration::updateValue($key, $values, $html, $shop['id_shop_group'], $shop['id_shop']); + \Configuration::updateValue($key, $value, $html, $shop['id_shop_group'], $shop['id_shop']); } return; } - \Configuration::updateValue($key, $values, $html, $idShopGroup, $idShop); + \Configuration::updateValue($key, $value, $html, $idShopGroup, $idShop); + } + + /** + * @param string|array{production: string, sandbox: string} $key + */ + public function delete($key) + { + if (is_array($key)) { + if ((int) $this->get(Config::MOLLIE_ENVIRONMENT)) { + $key = $key['production']; + } else { + $key = $key['sandbox']; + } + } + + \Configuration::deleteByName($key); } } diff --git a/src/Adapter/Context.php b/src/Adapter/Context.php index 3daa86e72..7ec026b26 100644 --- a/src/Adapter/Context.php +++ b/src/Adapter/Context.php @@ -12,6 +12,7 @@ namespace Mollie\Adapter; +use Configuration as PrestashopConfiguration; use Context as PrestashopContext; class Context @@ -60,4 +61,68 @@ public function getCartProducts(): array { return PrestashopContext::getContext()->cart->getProducts(); } + + public function getComputingPrecision(): int + { + if (method_exists(PrestashopContext::getContext(), 'getComputingPrecision')) { + return PrestashopContext::getContext()->getComputingPrecision(); + } + + return (int) PrestashopConfiguration::get('PS_PRICE_DISPLAY_PRECISION'); + } + + public function getProductLink($product): string + { + return (string) PrestashopContext::getContext()->link->getProductLink($product); + } + + public function getImageLink($name, $ids, $type = null): string + { + return (string) PrestashopContext::getContext()->link->getImageLink($name, $ids, $type); + } + + public function getShopId(): int + { + return (int) PrestashopContext::getContext()->shop->id; + } + + public function getCustomerAddressInvoiceId(): int + { + return (int) PrestashopContext::getContext()->cart->id_address_invoice; + } + + public function getModuleLink( + $module, + $controller = 'default', + array $params = [], + $ssl = null, + $idLang = null, + $idShop = null, + $relativeProtocol = false + ): string { + return (string) PrestashopContext::getContext()->link->getModuleLink( + $module, + $controller, + $params, + $ssl, + $idLang, + $idShop, + $relativeProtocol + ); + } + + public function getAddressInvoiceId(): int + { + return (int) PrestashopContext::getContext()->cart->id_address_invoice; + } + + public function getLanguageLocale(): string + { + return (string) PrestashopContext::getContext()->language->locale; + } + + public function getCountryId(): int + { + return (int) PrestashopContext::getContext()->country->id; + } } diff --git a/src/Application/CommandHandler/UpdateApplePayShippingContactHandler.php b/src/Application/CommandHandler/UpdateApplePayShippingContactHandler.php index 876cbc2b3..25c64c118 100644 --- a/src/Application/CommandHandler/UpdateApplePayShippingContactHandler.php +++ b/src/Application/CommandHandler/UpdateApplePayShippingContactHandler.php @@ -21,7 +21,7 @@ use Mollie\Builder\ApplePayDirect\ApplePayCarriersBuilder; use Mollie\Collector\ApplePayDirect\OrderTotalCollector; use Mollie\Config\Config; -use Mollie\Service\OrderFeeService; +use Mollie\Service\OrderPaymentFeeService; use Mollie\Utility\ApplePayDirect\ShippingMethodUtility; use Tools; @@ -33,19 +33,19 @@ final class UpdateApplePayShippingContactHandler private $applePayCarriersBuilder; /** - * @var OrderFeeService + * @var OrderPaymentFeeService */ - private $orderFeeService; + private $orderPaymentFeeService; /** @var OrderTotalCollector */ private $orderTotalCollector; public function __construct( ApplePayCarriersBuilder $applePayCarriersBuilder, - OrderFeeService $orderFeeService, + OrderPaymentFeeService $orderPaymentFeeService, OrderTotalCollector $orderTotalCollector ) { $this->applePayCarriersBuilder = $applePayCarriersBuilder; - $this->orderFeeService = $orderFeeService; + $this->orderPaymentFeeService = $orderPaymentFeeService; $this->orderTotalCollector = $orderTotalCollector; } @@ -65,8 +65,11 @@ public function handle(UpdateApplePayShippingContact $command): array $totals = $this->orderTotalCollector->getOrderTotals($applePayCarriers, $cart); $paymentFee = 0; + if ($totals) { - $paymentFee = $this->orderFeeService->getPaymentFee($totals[0]['amountWithoutFee'], Config::APPLEPAY); + $paymentFeeData = $this->orderPaymentFeeService->getPaymentFee($totals[0]['amountWithoutFee'], Config::APPLEPAY); + + $paymentFee = $paymentFeeData->getPaymentFeeTaxIncl(); } return [ diff --git a/src/Application/CommandHandler/UpdateApplePayShippingMethodHandler.php b/src/Application/CommandHandler/UpdateApplePayShippingMethodHandler.php index 9f8a0761a..38f7ab75b 100644 --- a/src/Application/CommandHandler/UpdateApplePayShippingMethodHandler.php +++ b/src/Application/CommandHandler/UpdateApplePayShippingMethodHandler.php @@ -15,35 +15,41 @@ use Cart; use Mollie\Application\Command\UpdateApplePayShippingMethod; use Mollie\Config\Config; -use Mollie\Service\OrderFeeService; +use Mollie\Service\OrderPaymentFeeService; final class UpdateApplePayShippingMethodHandler { /** - * @var OrderFeeService + * @var OrderPaymentFeeService */ - private $orderFeeService; + private $orderPaymentFeeService; - public function __construct(OrderFeeService $orderFeeService) + public function __construct(OrderPaymentFeeService $orderPaymentFeeService) { - $this->orderFeeService = $orderFeeService; + $this->orderPaymentFeeService = $orderPaymentFeeService; } public function handle(UpdateApplePayShippingMethod $command): array { $cart = new Cart($command->getCartId()); + $cart->id_carrier = $command->getCarrierId(); $cart->setDeliveryOption([ $cart->id_address_delivery => $command->getCarrierId() . ',', ]); + $cart->update(); - $orderTotal = $cart->getOrderTotal(true, Cart::BOTH, null, $command->getCarrierId()); - $fee = $this->orderFeeService->getPaymentFee($orderTotal, Config::APPLEPAY); + + $orderTotal = (float) $cart->getOrderTotal(true, Cart::BOTH, null, $command->getCarrierId()); + + $paymentFeeData = $this->orderPaymentFeeService->getPaymentFee($orderTotal, Config::APPLEPAY); + + $paymentFee = $paymentFeeData->getPaymentFeeTaxIncl(); return [ 'success' => true, - 'data' => [ - 'amount' => $orderTotal + $fee, + 'data' => [// TODO use calculator + 'amount' => $orderTotal + $paymentFee, ], ]; } diff --git a/src/Builder/Content/BaseInfoBlock.php b/src/Builder/Content/BaseInfoBlock.php index 40a81fe9c..4c5f878d6 100644 --- a/src/Builder/Content/BaseInfoBlock.php +++ b/src/Builder/Content/BaseInfoBlock.php @@ -38,7 +38,6 @@ public function buildParams() 'path' => $this->module->getPathUri(), 'payscreen_locale_value' => Configuration::get(Mollie\Config\Config::MOLLIE_PAYMENTSCREEN_LOCALE), 'val_images' => Configuration::get(Mollie\Config\Config::MOLLIE_IMAGES), - 'val_issuers' => Configuration::get(Mollie\Config\Config::MOLLIE_ISSUERS), 'val_css' => Configuration::get(Mollie\Config\Config::MOLLIE_CSS), 'val_errors' => Configuration::get(Mollie\Config\Config::MOLLIE_DISPLAY_ERRORS), 'val_logger' => Configuration::get(Mollie\Config\Config::MOLLIE_DEBUG_LOG), diff --git a/src/Builder/FormBuilder.php b/src/Builder/FormBuilder.php index c1c17a7d4..5211a9ee0 100644 --- a/src/Builder/FormBuilder.php +++ b/src/Builder/FormBuilder.php @@ -15,6 +15,7 @@ use HelperFormCore as HelperForm; use Mollie; use Mollie\Adapter\ConfigurationAdapter; +use Mollie\Adapter\Context; use Mollie\Adapter\Language; use Mollie\Adapter\Link; use Mollie\Adapter\Smarty; @@ -23,6 +24,7 @@ use Mollie\Api\Types\RefundStatus; use Mollie\Config\Config; use Mollie\Provider\CustomLogoProviderInterface; +use Mollie\Repository\TaxRulesGroupRepositoryInterface; use Mollie\Service\ApiService; use Mollie\Service\ConfigFieldService; use Mollie\Service\CountryService; @@ -83,6 +85,12 @@ class FormBuilder */ private $configuration; + /** @var TaxRulesGroupRepositoryInterface */ + private $taxRulesGroupRepository; + + /** @var Context */ + private $context; + public function __construct( Mollie $module, ApiService $apiService, @@ -93,7 +101,9 @@ public function __construct( Smarty $smarty, Link $link, CustomLogoProviderInterface $creditCardLogoProvider, - ConfigurationAdapter $configuration + ConfigurationAdapter $configuration, + TaxRulesGroupRepositoryInterface $taxRulesGroupRepository, + Context $context ) { $this->module = $module; $this->apiService = $apiService; @@ -105,6 +115,8 @@ public function __construct( $this->carrierInformationService = $carrierInformationService; $this->creditCardLogoProvider = $creditCardLogoProvider; $this->configuration = $configuration; + $this->taxRulesGroupRepository = $taxRulesGroupRepository; + $this->context = $context; } public function buildSettingsForm() @@ -309,7 +321,7 @@ protected function getAccountSettingsSection($isApiKeyProvided) 'type' => 'switch', 'label' => $this->module->l('Use Mollie Components for credit cards', self::FILE_NAME), 'tab' => $generalSettings, - 'name' => Config::MOLLIE_IFRAME, + 'name' => Config::MOLLIE_IFRAME[(int) $this->configuration->get(Config::MOLLIE_ENVIRONMENT) ? 'production' : 'sandbox'], 'desc' => TagsUtility::ppTags( $this->module->l('Read more about [1]Mollie Components[/1] and how it improves your conversion.', self::FILE_NAME), [$this->module->display($this->module->getPathUri(), 'views/templates/admin/mollie_components_info.tpl')] @@ -334,7 +346,7 @@ protected function getAccountSettingsSection($isApiKeyProvided) 'type' => 'switch', 'label' => $this->module->l('Use one-click payments for credit cards', self::FILE_NAME), 'tab' => $generalSettings, - 'name' => Config::MOLLIE_SINGLE_CLICK_PAYMENT, + 'name' => Config::MOLLIE_SINGLE_CLICK_PAYMENT[(int) $this->configuration->get(Config::MOLLIE_ENVIRONMENT) ? 'production' : 'sandbox'], 'desc' => TagsUtility::ppTags( $this->module->l('Read more about [1]Single Click Payments[/1] and how it improves your conversion.', self::FILE_NAME), [ @@ -362,7 +374,7 @@ protected function getAccountSettingsSection($isApiKeyProvided) 'label' => $this->module->l('Issuer list', self::FILE_NAME), 'tab' => $generalSettings, 'desc' => $this->module->l('Some payment methods (e.g. iDEAL) have an issuer list. Select where to display the list.', self::FILE_NAME), - 'name' => Config::MOLLIE_ISSUERS, + 'name' => Config::MOLLIE_ISSUERS[(int) $this->configuration->get(Config::MOLLIE_ENVIRONMENT) ? 'production' : 'sandbox'], 'options' => [ 'query' => [ [ @@ -387,6 +399,7 @@ protected function getAccountSettingsSection($isApiKeyProvided) 'name' => '', 'title' => $this->module->l('Payment methods', self::FILE_NAME), ]; + $molliePaymentMethods = $this->apiService->getMethodsForConfig($this->module->getApiClient()); if (empty($molliePaymentMethods)) { @@ -403,6 +416,7 @@ protected function getAccountSettingsSection($isApiKeyProvided) 'name' => Config::METHODS_CONFIG, 'paymentMethods' => $molliePaymentMethods, 'countries' => $this->countryService->getActiveCountriesList(), + 'taxRulesGroups' => $this->taxRulesGroupRepository->getTaxRulesGroups($this->context->getShopId()), 'tab' => $generalSettings, 'onlyOrderMethods' => Config::ORDER_API_ONLY_METHODS, 'displayErrors' => $this->configuration->get(Config::MOLLIE_DISPLAY_ERRORS), diff --git a/src/Builder/InvoicePdfTemplateBuilder.php b/src/Builder/InvoicePdfTemplateBuilder.php index 4231bfd7d..b638e70a5 100644 --- a/src/Builder/InvoicePdfTemplateBuilder.php +++ b/src/Builder/InvoicePdfTemplateBuilder.php @@ -12,48 +12,74 @@ namespace Mollie\Builder; -use Cart; use Currency; -use Mollie\Repository\OrderFeeRepository; -use MolOrderFee; +use Mollie\Repository\CurrencyRepositoryInterface; +use Mollie\Repository\MolOrderPaymentFeeRepositoryInterface; +use MolOrderPaymentFee; use Order; -use Tools; +use PrestaShop\PrestaShop\Core\Localization\Locale; final class InvoicePdfTemplateBuilder implements TemplateBuilderInterface { - private $orderFeeRepository; - /** * @var Order */ private $order; + /** @var MolOrderPaymentFeeRepositoryInterface */ + private $molOrderPaymentFeeRepository; + /** @var Locale */ + private $locale; + /** @var CurrencyRepositoryInterface */ + private $currencyRepository; - public function __construct(OrderFeeRepository $orderFeeRepository) - { - $this->orderFeeRepository = $orderFeeRepository; + public function __construct( + MolOrderPaymentFeeRepositoryInterface $molOrderPaymentFeeRepository, + CurrencyRepositoryInterface $currencyRepository + ) { + $this->molOrderPaymentFeeRepository = $molOrderPaymentFeeRepository; + $this->currencyRepository = $currencyRepository; } - public function setOrder(Order $order) + public function setOrder(Order $order): InvoicePdfTemplateBuilder { $this->order = $order; return $this; } - public function buildParams() + public function setLocale(Locale $locale): InvoicePdfTemplateBuilder + { + $this->locale = $locale; + + return $this; + } + + public function buildParams(): array { - $orderFeeId = $this->orderFeeRepository->getOrderFeeIdByCartId(Cart::getCartIdByOrderId($this->order->id)); + /** @var MolOrderPaymentFee|null $molOrderPaymentFee */ + $molOrderPaymentFee = $this->molOrderPaymentFeeRepository->findOneBy([ + 'id_order' => (int) $this->order->id, + ]); + + if (!$molOrderPaymentFee) { + return []; + } - $orderFee = new MolOrderFee($orderFeeId); + /** @var Currency|null $orderCurrency */ + $orderCurrency = $this->currencyRepository->findOneBy([ + 'id_currency' => $this->order->id_currency, + 'deleted' => 0, + 'active' => 1, + ]); - if (!$orderFee->order_fee) { + if (!$orderCurrency) { return []; } return [ - 'orderFeeAmountDisplay' => Tools::displayPrice( - $orderFee->order_fee, - new Currency($this->order->id_currency) + 'orderFeeAmountDisplay' => $this->locale->formatPrice( + $molOrderPaymentFee->fee_tax_incl, + $orderCurrency->iso_code ), ]; } diff --git a/src/Calculator/PaymentFeeCalculator.php b/src/Calculator/PaymentFeeCalculator.php new file mode 100644 index 000000000..44b7ec448 --- /dev/null +++ b/src/Calculator/PaymentFeeCalculator.php @@ -0,0 +1,123 @@ +taxCalculator = $taxCalculator; + $this->context = $context; + } + + public function calculateFixedFee(float $totalFeePriceTaxExcl): PaymentFeeData + { + $totalFeePriceTaxIncl = $this->taxCalculator->addTaxes($totalFeePriceTaxExcl); + + return $this->buildPaymentFee( + $totalFeePriceTaxIncl, + $totalFeePriceTaxExcl + ); + } + + public function calculatePercentageFee( + float $totalCartPriceTaxIncl, + float $surchargePercentage, + float $surchargeLimit + ): PaymentFeeData { + $totalFeePriceTaxIncl = NumberUtility::times( + $totalCartPriceTaxIncl, + NumberUtility::divide($surchargePercentage, self::MAX_PERCENTAGE) + ); + + if ($this->isPaymentFeeGreaterThanMaxLimit( + $totalFeePriceTaxIncl, + $surchargeLimit + )) { + return $this->calculateSurchargeMaxValue($surchargeLimit); + } + + $totalFeePriceTaxExcl = $this->taxCalculator->removeTaxes($totalFeePriceTaxIncl); + + return $this->buildPaymentFee( + $totalFeePriceTaxIncl, + $totalFeePriceTaxExcl + ); + } + + public function calculatePercentageAndFixedPriceFee( + float $totalCartPriceTaxIncl, + float $surchargePercentage, + float $surchargeFixedPriceTaxExcl, + float $surchargeLimit + ): PaymentFeeData { + $surchargeFixedPriceTaxIncl = $this->taxCalculator->addTaxes($surchargeFixedPriceTaxExcl); + + $totalFeePriceTaxIncl = NumberUtility::plus(NumberUtility::times( + $totalCartPriceTaxIncl, + NumberUtility::divide($surchargePercentage, self::MAX_PERCENTAGE) + ), $surchargeFixedPriceTaxIncl); + + if ($this->isPaymentFeeGreaterThanMaxLimit( + $totalFeePriceTaxIncl, + $surchargeLimit + )) { + return $this->calculateSurchargeMaxValue($surchargeLimit); + } + + $totalFeePriceTaxExcl = $this->taxCalculator->removeTaxes($totalFeePriceTaxIncl); + + return $this->buildPaymentFee( + $totalFeePriceTaxIncl, + $totalFeePriceTaxExcl + ); + } + + private function calculateSurchargeMaxValue(float $surchargeMaxValue): PaymentFeeData + { + $totalFeePriceTaxIncl = $surchargeMaxValue; + $totalFeePriceTaxExcl = $this->taxCalculator->removeTaxes($totalFeePriceTaxIncl); + + return $this->buildPaymentFee( + $totalFeePriceTaxIncl, + $totalFeePriceTaxExcl + ); + } + + private function isPaymentFeeGreaterThanMaxLimit( + float $totalFeePriceTaxIncl, + float $surchargeLimit + ): bool { + if (NumberUtility::isGreaterThan($totalFeePriceTaxIncl, $surchargeLimit)) { + return true; + } + + return false; + } + + private function buildPaymentFee( + float $totalFeePriceTaxIncl, + float $totalFeePriceTaxExcl + ): PaymentFeeData { + $isPaymentFeeActive = $totalFeePriceTaxIncl > 0 && $totalFeePriceTaxExcl > 0; + + return new PaymentFeeData( + NumberUtility::toPrecision($totalFeePriceTaxIncl, $this->context->getComputingPrecision()), + NumberUtility::toPrecision($totalFeePriceTaxExcl, $this->context->getComputingPrecision()), + $this->taxCalculator->getTotalRate(), + $isPaymentFeeActive + ); + } +} diff --git a/src/Collector/ApplePayDirect/OrderTotalCollector.php b/src/Collector/ApplePayDirect/OrderTotalCollector.php index 5eacf6728..58375ed27 100644 --- a/src/Collector/ApplePayDirect/OrderTotalCollector.php +++ b/src/Collector/ApplePayDirect/OrderTotalCollector.php @@ -15,16 +15,16 @@ use Cart; use Mollie\Config\Config; use Mollie\DTO\ApplePay\Carrier\Carrier as AppleCarrier; -use Mollie\Service\OrderFeeService; +use Mollie\Service\OrderPaymentFeeService; class OrderTotalCollector { - /** @var OrderFeeService */ - private $orderFeeService; + /** @var OrderPaymentFeeService */ + private $orderPaymentFeeService; - public function __construct(OrderFeeService $orderFeeService) + public function __construct(OrderPaymentFeeService $orderPaymentFeeService) { - $this->orderFeeService = $orderFeeService; + $this->orderPaymentFeeService = $orderPaymentFeeService; } /** @@ -39,7 +39,10 @@ public function getOrderTotals($applePayCarriers, Cart $cart) { return array_map(function (AppleCarrier $carrier) use ($cart) { $orderTotal = (float) number_format($cart->getOrderTotal(true, Cart::BOTH, null, $carrier->getCarrierId()), 2, '.', ''); - $paymentFee = $this->orderFeeService->getPaymentFee($orderTotal, Config::APPLEPAY); + + $paymentFeeData = $this->orderPaymentFeeService->getPaymentFee($orderTotal, Config::APPLEPAY); + + $paymentFee = $paymentFeeData->getPaymentFeeTaxIncl(); return [ 'type' => 'final', diff --git a/src/Config/Config.php b/src/Config/Config.php index 6d1038067..ef6d5a140 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -98,11 +98,20 @@ class Config const MOLLIE_ACCOUNT_SWITCH = 'MOLLIE_ACCOUNT_SWITCH'; const MOLLIE_PAYMENTSCREEN_LOCALE = 'MOLLIE_PAYMENTSCREEN_LOCALE'; const MOLLIE_SEND_ORDER_CONFIRMATION = 'MOLLIE_SEND_ORDER_CONFIRMATION'; - const MOLLIE_IFRAME = 'MOLLIE_IFRAME'; - const MOLLIE_SINGLE_CLICK_PAYMENT = 'MOLLIE_SINGLE_CLICK_PAYMENT'; + const MOLLIE_IFRAME = [ + 'sandbox' => 'MOLLIE_SANDBOX_IFRAME', + 'production' => 'MOLLIE_PRODUCTION_IFRAME', + ]; + const MOLLIE_SINGLE_CLICK_PAYMENT = [ + 'sandbox' => 'MOLLIE_SANDBOX_SINGLE_CLICK_PAYMENT', + 'production' => 'MOLLIE_PRODUCTION_SINGLE_CLICK_PAYMENT', + ]; const MOLLIE_IMAGES = 'MOLLIE_IMAGES'; const MOLLIE_SHOW_RESEND_PAYMENT_LINK = 'MOLLIE_SHOW_RESEND_PAYMENT_LINK'; - const MOLLIE_ISSUERS = 'MOLLIE_ISSUERS'; + const MOLLIE_ISSUERS = [ + 'sandbox' => 'MOLLIE_SANDBOX_ISSUERS', + 'production' => 'MOLLIE_PRODUCTION_ISSUERS', + ]; const MOLLIE_CSS = 'MOLLIE_CSS'; const MOLLIE_DEBUG_LOG = 'MOLLIE_DEBUG_LOG'; const MOLLIE_METHOD_COUNTRIES = 'MOLLIE_METHOD_COUNTRIES'; @@ -157,7 +166,9 @@ class Config const MOLLIE_METHOD_MINIMUM_ORDER_VALUE = 'MOLLIE_METHOD_MINIMUM_ORDER_VALUE_'; const MOLLIE_METHOD_MAX_ORDER_VALUE = 'MOLLIE_METHOD_MAX_ORDER_VALUE_'; const MOLLIE_METHOD_SURCHARGE_TYPE = 'MOLLIE_METHOD_SURCHARGE_TYPE_'; - const MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT = 'MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_'; + const MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL = 'MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_INCL_'; + const MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL = 'MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL_'; + const MOLLIE_METHOD_TAX_RULES_GROUP_ID = 'MOLLIE_METHOD_TAX_RULES_GROUP_ID_'; const MOLLIE_METHOD_SURCHARGE_PERCENTAGE = 'MOLLIE_METHOD_SURCHARGE_PERCENTAGE_'; const MOLLIE_METHOD_SURCHARGE_LIMIT = 'MOLLIE_METHOD_SURCHARGE_LIMIT_'; const MOLLIE_METHOD_MIN_AMOUNT = 'MOLLIE_METHOD_MIN_AMOUNT_'; diff --git a/src/Controller/AbstractMollieController.php b/src/Controller/AbstractMollieController.php index c9f1833cd..2b2a98909 100644 --- a/src/Controller/AbstractMollieController.php +++ b/src/Controller/AbstractMollieController.php @@ -16,7 +16,7 @@ class AbstractMollieController extends \ModuleFrontControllerCore { - protected function respond($status, $statusCode = HttpStatusCode::HTTP_OK, $message = '') + protected function respond($status, $statusCode = HttpStatusCode::HTTP_OK, $message = ''): void { http_response_code($statusCode); @@ -26,6 +26,13 @@ protected function respond($status, $statusCode = HttpStatusCode::HTTP_OK, $mess $response['error'] = new Error($statusCode, $message); } - $this->ajaxDie(json_encode($response)); + $this->ajaxRender(json_encode($response)); + } + + protected function ajaxRender($value = null, $controller = null, $method = null): void + { + parent::ajaxRender($value, $controller, $method); + + exit; } } diff --git a/src/DTO/OrderData.php b/src/DTO/OrderData.php index 567fb5230..e3fa2e32f 100644 --- a/src/DTO/OrderData.php +++ b/src/DTO/OrderData.php @@ -114,6 +114,12 @@ class OrderData implements JsonSerializable */ private $sequenceType; + /** @var ?string */ + private $consumerDateOfBirth; + + /** @var ?string */ + private $title; + public function __construct( Amount $amount, $redirectUrl, @@ -372,6 +378,19 @@ public function setPayment($payment) $this->payment = $payment; } + public function getConsumerDateOfBirth(): ?string + { + return $this->consumerDateOfBirth; + } + + /** + * @param string $consumerDateOfBirth + */ + public function setConsumerDateOfBirth(string $consumerDateOfBirth): void + { + $this->consumerDateOfBirth = $consumerDateOfBirth; + } + public function getSequenceType(): string { return $this->sequenceType; @@ -382,6 +401,22 @@ public function setSequenceType(string $sequenceType): void $this->sequenceType = $sequenceType; } + /** + * @return string|null + */ + public function getTitle(): ?string + { + return $this->title; + } + + /** + * @param string|null $title + */ + public function setTitle(?string $title): void + { + $this->title = $title; + } + public function jsonSerialize() { $lines = []; @@ -404,6 +439,7 @@ public function jsonSerialize() 'givenName' => $this->cleanUpInput($this->getBillingAddress()->firstname), 'familyName' => $this->cleanUpInput($this->getBillingAddress()->lastname), 'email' => $this->cleanUpInput($this->getEmail()), + 'title' => $this->cleanUpInput($this->getTitle()), ], 'shippingAddress' => [ 'organizationName' => $this->cleanUpInput($this->getShippingAddress()->company), @@ -415,6 +451,7 @@ public function jsonSerialize() 'givenName' => $this->cleanUpInput($this->getShippingAddress()->firstname), 'familyName' => $this->cleanUpInput($this->getShippingAddress()->lastname), 'email' => $this->cleanUpInput($this->getEmail()), + 'title' => $this->cleanUpInput($this->getTitle()), ], 'redirectUrl' => $this->getRedirectUrl(), 'webhookUrl' => $this->getWebhookUrl(), @@ -424,6 +461,7 @@ public function jsonSerialize() 'orderNumber' => $this->getOrderNumber(), 'lines' => $lines, 'payment' => $this->getPayment(), + 'consumerDateOfBirth' => $this->getConsumerDateOfBirth(), ]; if ($this->billingPhoneNumber) { diff --git a/src/DTO/OrderStateData.php b/src/DTO/OrderStateData.php new file mode 100644 index 000000000..10fbebe7c --- /dev/null +++ b/src/DTO/OrderStateData.php @@ -0,0 +1,131 @@ +name = $name; + $this->sendEmail = $sendEmail; + $this->color = $color; + $this->logable = $logable; + $this->delivery = $delivery; + $this->invoice = $invoice; + $this->shipped = $shipped; + $this->paid = $paid; + $this->template = $template; + $this->pdfInvoice = $pdfInvoice; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return bool + */ + public function isSendEmail(): bool + { + return $this->sendEmail; + } + + /** + * @return string + */ + public function getColor(): string + { + return $this->color; + } + + /** + * @return bool + */ + public function isLogable(): bool + { + return $this->logable; + } + + /** + * @return bool + */ + public function isDelivery(): bool + { + return $this->delivery; + } + + /** + * @return bool + */ + public function isInvoice(): bool + { + return $this->invoice; + } + + /** + * @return bool + */ + public function isShipped(): bool + { + return $this->shipped; + } + + /** + * @return bool + */ + public function isPaid(): bool + { + return $this->paid; + } + + /** + * @return string + */ + public function getTemplate(): string + { + return $this->template; + } + + /** + * @return bool + */ + public function isPdfInvoice(): bool + { + return $this->pdfInvoice; + } +} diff --git a/src/DTO/PaymentFeeData.php b/src/DTO/PaymentFeeData.php new file mode 100644 index 000000000..cd8cbe2b9 --- /dev/null +++ b/src/DTO/PaymentFeeData.php @@ -0,0 +1,59 @@ +paymentFeeTaxIncl = $paymentFeeTaxIncl; + $this->paymentFeeTaxExcl = $paymentFeeTaxExcl; + $this->taxRate = $taxRate; + $this->active = $active; + } + + /** + * @return float + */ + public function getPaymentFeeTaxIncl(): float + { + return $this->paymentFeeTaxIncl; + } + + /** + * @return float + */ + public function getPaymentFeeTaxExcl(): float + { + return $this->paymentFeeTaxExcl; + } + + /** + * @return float + */ + public function getTaxRate(): float + { + return $this->taxRate; + } + + /** + * @return bool + */ + public function isActive(): bool + { + return $this->active; + } +} diff --git a/src/Entity/MolOrderFee.php b/src/Entity/MolOrderPaymentFee.php similarity index 51% rename from src/Entity/MolOrderFee.php rename to src/Entity/MolOrderPaymentFee.php index 6334580cc..703b316ba 100644 --- a/src/Entity/MolOrderFee.php +++ b/src/Entity/MolOrderPaymentFee.php @@ -9,27 +9,39 @@ * @see https://github.com/mollie/PrestaShop * @codingStandardsIgnoreStart */ -class MolOrderFee extends ObjectModel +class MolOrderPaymentFee extends ObjectModel { /** * @var int */ public $id_cart; + /** + * @var int + */ + public $id_order; + + /** + * @var float + */ + public $fee_tax_incl; + /** * @var float */ - public $order_fee; + public $fee_tax_excl; /** * @var array */ public static $definition = [ - 'table' => 'mol_order_fee', - 'primary' => 'id_mol_order_fee', + 'table' => 'mol_order_payment_fee', + 'primary' => 'id_mol_order_payment_fee', 'fields' => [ 'id_cart' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], - 'order_fee' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'], + 'id_order' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], + 'fee_tax_incl' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'], + 'fee_tax_excl' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'], ], ]; } diff --git a/src/Entity/MolPaymentMethod.php b/src/Entity/MolPaymentMethod.php index 5eaefa645..f960ca063 100644 --- a/src/Entity/MolPaymentMethod.php +++ b/src/Entity/MolPaymentMethod.php @@ -62,9 +62,14 @@ class MolPaymentMethod extends ObjectModel public $surcharge; /** - * @var string + * @var float + */ + public $surcharge_fixed_amount_tax_excl; + + /** + * @var int */ - public $surcharge_fixed_amount; + public $tax_rules_group_id; /** * @var string @@ -121,7 +126,8 @@ class MolPaymentMethod extends ObjectModel 'minimal_order_value' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'], 'max_order_value' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'], 'surcharge' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], - 'surcharge_fixed_amount' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'], + 'surcharge_fixed_amount_tax_excl' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'], + 'tax_rules_group_id' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], 'surcharge_percentage' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'], 'surcharge_limit' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'], 'images_json' => ['type' => self::TYPE_STRING, 'validate' => 'isString'], diff --git a/src/Enum/EmailTemplate.php b/src/Enum/EmailTemplate.php new file mode 100644 index 000000000..40c556fc8 --- /dev/null +++ b/src/Enum/EmailTemplate.php @@ -0,0 +1,9 @@ +module = $module; $this->paymentMethodRepository = $paymentMethodRepository; $this->paymentMethodService = $paymentMethodService; - $this->orderFeeHandler = $orderFeeHandler; + $this->orderPaymentFeeHandler = $orderPaymentFeeHandler; $this->orderStatusService = $orderStatusService; $this->recurringOrderCreation = $recurringOrderCreation; $this->subscriptionOrder = $subscriptionOrder; + $this->paymentFeeProvider = $paymentFeeProvider; } /** * @param MollieOrderAlias|MolliePaymentAlias $apiPayment + * @param int $cartId + * @param bool $isKlarnaOrder + * + * @return int + * + * @throws FailedToProvidePaymentFeeException + * @throws ApiException + * @throws OrderCreationException + * @throws \PrestaShopDatabaseException + * @throws \PrestaShopException */ public function createOrder($apiPayment, int $cartId, $isKlarnaOrder = false): int { @@ -109,27 +126,21 @@ public function createOrder($apiPayment, int $cartId, $isKlarnaOrder = false): i (int) Config::getStatuses()[PaymentStatus::STATUS_PAID]; $cart = new Cart($cartId); + $originalAmount = $cart->getOrderTotal( true, Cart::BOTH ); - $paymentFee = 0; $paymentMethod = $this->paymentMethodService->getPaymentMethod($apiPayment); - if ($apiPayment->resource === Config::MOLLIE_API_STATUS_PAYMENT) { - $paymentFee = PaymentFeeUtility::getPaymentFee($paymentMethod, $originalAmount); - } else { - /** @var Mollie\Api\Resources\OrderLine $line */ - foreach ($apiPayment->lines() as $line) { - if ($line->sku === Config::PAYMENT_FEE_SKU) { - $paymentFee = $line->totalAmount->value; - } - } - } + + $paymentFeeData = $this->paymentFeeProvider->getPaymentFee($paymentMethod, (float) $originalAmount); + if (Order::getOrderByCartId((int) $cartId)) { return 0; } - if (!$paymentFee) { + + if (!$paymentFeeData->isActive()) { $this->module->validateOrder( (int) $cartId, $orderStatus, @@ -149,9 +160,11 @@ public function createOrder($apiPayment, int $cartId, $isKlarnaOrder = false): i return $orderId; } - $cartPrice = NumberUtility::plus($originalAmount, $paymentFee); + + $cartPrice = NumberUtility::plus($originalAmount, $paymentFeeData->getPaymentFeeTaxIncl()); $priceDifference = NumberUtility::minus($cartPrice, $apiPayment->amount->value); - if (abs($priceDifference) > 0.01) { + + if (abs($priceDifference) !== 0.00) { if ($apiPayment->resource === Config::MOLLIE_API_STATUS_ORDER) { $apiPayment->refundAll(); } else { @@ -162,6 +175,7 @@ public function createOrder($apiPayment, int $cartId, $isKlarnaOrder = false): i ], ]); } + $this->paymentMethodRepository->updatePaymentReason($apiPayment->id, Config::WRONG_AMOUNT_REASON); throw new \Exception('Wrong cart amount'); @@ -181,7 +195,8 @@ public function createOrder($apiPayment, int $cartId, $isKlarnaOrder = false): i /* @phpstan-ignore-next-line */ $orderId = (int) Order::getOrderByCartId((int) $cartId); - $this->orderFeeHandler->addOrderFee($orderId, $apiPayment); + + $this->orderPaymentFeeHandler->addOrderPaymentFee($orderId, $apiPayment); $this->orderStatusService->setOrderStatus($orderId, $orderStatus); @@ -237,7 +252,10 @@ public function createBankTransferOrder($paymentData, Cart $cart) $paymentMethod = new MolPaymentMethod( $paymentMethodRepository->getPaymentMethodIdByMethodId($paymentData->getMethod(), $environment) ); - $paymentFee = PaymentFeeUtility::getPaymentFee($paymentMethod, $originalAmount); + + $paymentFeeData = $this->paymentFeeProvider->getPaymentFee($paymentMethod, (float) $originalAmount); + + $paymentFee = $paymentFeeData->getPaymentFeeTaxIncl(); } else { /** @var Line $line */ foreach ($paymentData->getLines() as $line) { diff --git a/src/Handler/Order/OrderFeeHandler.php b/src/Handler/Order/OrderPaymentFeeHandler.php similarity index 77% rename from src/Handler/Order/OrderFeeHandler.php rename to src/Handler/Order/OrderPaymentFeeHandler.php index 6cb4cea54..2866045b6 100644 --- a/src/Handler/Order/OrderFeeHandler.php +++ b/src/Handler/Order/OrderPaymentFeeHandler.php @@ -38,64 +38,60 @@ use Cart; use Configuration; -use Mollie\Api\Resources\OrderLine; -use Mollie\Config\Config; -use Mollie\Service\OrderFeeService; +use Mollie\Provider\PaymentFeeProviderInterface; +use Mollie\Service\OrderPaymentFeeService; use Mollie\Service\PaymentMethodService; -use Mollie\Utility\PaymentFeeUtility; use Order; use OrderDetail; use PrestaShop\Decimal\Number; -class OrderFeeHandler +class OrderPaymentFeeHandler { - /** @var OrderFeeService */ - private $feeService; + /** @var OrderPaymentFeeService */ + private $orderPaymentFeeService; /** @var PaymentMethodService */ private $paymentMethodService; + /** @var PaymentFeeProviderInterface */ + private $paymentFeeProvider; public function __construct( - OrderFeeService $feeService, - PaymentMethodService $paymentMethodService + OrderPaymentFeeService $orderPaymentFeeService, + PaymentMethodService $paymentMethodService, + PaymentFeeProviderInterface $paymentFeeProvider ) { - $this->feeService = $feeService; + $this->orderPaymentFeeService = $orderPaymentFeeService; $this->paymentMethodService = $paymentMethodService; + $this->paymentFeeProvider = $paymentFeeProvider; } - public function addOrderFee(int $orderId, $apiPayment) + public function addOrderPaymentFee(int $orderId, $apiPayment): int { $order = new Order($orderId); - $cart = new Cart($order->id_cart); + $originalAmountWithTax = $cart->getOrderTotal( true, Cart::BOTH ); + $originalAmountWithoutTax = $cart->getOrderTotal( false, Cart::BOTH ); - $paymentFee = 0; $paymentMethod = $this->paymentMethodService->getPaymentMethod($apiPayment); - if ($apiPayment->resource === Config::MOLLIE_API_STATUS_PAYMENT) { - $paymentFee = PaymentFeeUtility::getPaymentFee($paymentMethod, $originalAmountWithTax); - } else { - /** @var OrderLine $line */ - foreach ($apiPayment->lines() as $line) { - if ($line->sku === Config::PAYMENT_FEE_SKU) { - $paymentFee = $line->totalAmount->value; - } - } - } - $this->feeService->createOrderFee($order->id_cart, $paymentFee); + $paymentFeeData = $this->paymentFeeProvider->getPaymentFee($paymentMethod, (float) $originalAmountWithTax); + + $this->orderPaymentFeeService->createOrderPaymentFee($orderId, (int) $order->id_cart, $paymentFeeData); $order = new Order($orderId); - $order->total_paid_tax_excl = (float) (new Number((string) $originalAmountWithoutTax))->plus((new Number((string) $paymentFee)))->toPrecision(2); - $order->total_paid_tax_incl = (float) (new Number((string) $originalAmountWithTax))->plus((new Number((string) $paymentFee)))->toPrecision(2); + + $order->total_paid_tax_excl = (float) (new Number((string) $originalAmountWithoutTax))->plus((new Number((string) $paymentFeeData->getPaymentFeeTaxExcl())))->toPrecision(2); + $order->total_paid_tax_incl = (float) (new Number((string) $originalAmountWithTax))->plus((new Number((string) $paymentFeeData->getPaymentFeeTaxIncl())))->toPrecision(2); $order->total_paid = (float) $apiPayment->amount->value; $order->total_paid_real = (float) $apiPayment->amount->value; + $order->update(); return $orderId; diff --git a/src/Handler/PaymentOption/PaymentOptionHandler.php b/src/Handler/PaymentOption/PaymentOptionHandler.php index 9f6c2887b..028f8cf5b 100644 --- a/src/Handler/PaymentOption/PaymentOptionHandler.php +++ b/src/Handler/PaymentOption/PaymentOptionHandler.php @@ -37,6 +37,7 @@ namespace Mollie\Handler\PaymentOption; use Configuration; +use Mollie\Adapter\ConfigurationAdapter; use Mollie\Api\Types\PaymentMethod; use Mollie\Config\Config; use Mollie\Provider\PaymentOption\BancontactPaymentOptionProvider; @@ -69,19 +70,23 @@ class PaymentOptionHandler implements PaymentOptionHandlerInterface private $cardSingleClickPaymentOptionProvider; /** @var BancontactPaymentOptionProvider */ private $bancontactPaymentOptionProvider; + /** @var ConfigurationAdapter */ + private $configurationAdapter; public function __construct( BasePaymentOptionProvider $basePaymentOptionProvider, CreditCardPaymentOptionProvider $creditCardPaymentOptionProvider, CreditCardSingleClickPaymentOptionProvider $cardSingleClickPaymentOptionProvider, IdealPaymentOptionProvider $idealPaymentOptionProvider, - BancontactPaymentOptionProvider $bancontactPaymentOptionProvider + BancontactPaymentOptionProvider $bancontactPaymentOptionProvider, + ConfigurationAdapter $configurationAdapter ) { $this->basePaymentOptionProvider = $basePaymentOptionProvider; $this->creditCardPaymentOptionProvider = $creditCardPaymentOptionProvider; $this->idealPaymentOptionProvider = $idealPaymentOptionProvider; $this->cardSingleClickPaymentOptionProvider = $cardSingleClickPaymentOptionProvider; $this->bancontactPaymentOptionProvider = $bancontactPaymentOptionProvider; + $this->configurationAdapter = $configurationAdapter; } /** @@ -118,7 +123,7 @@ private function isIdealPaymentMethod(MolPaymentMethod $paymentMethod) return false; } - if (Configuration::get(Config::MOLLIE_ISSUERS) !== Config::ISSUERS_ON_CLICK) { + if ($this->configurationAdapter->get(Config::MOLLIE_ISSUERS) !== Config::ISSUERS_ON_CLICK) { return false; } @@ -152,7 +157,7 @@ private function isBancontactWithQRCodePaymentMethod(MolPaymentMethod $paymentMe private function isIFrame() { - if (!Configuration::get(Config::MOLLIE_IFRAME)) { + if (!(int) $this->configurationAdapter->get(Config::MOLLIE_IFRAME)) { return false; } @@ -161,7 +166,7 @@ private function isIFrame() private function isSingleClick() { - if (!Configuration::get(Config::MOLLIE_SINGLE_CLICK_PAYMENT)) { + if (!(int) $this->configurationAdapter->get(Config::MOLLIE_SINGLE_CLICK_PAYMENT)) { return false; } diff --git a/src/Handler/Shipment/ShipmentSenderHandler.php b/src/Handler/Shipment/ShipmentSenderHandler.php index 21cdb56ac..2999e80d7 100644 --- a/src/Handler/Shipment/ShipmentSenderHandler.php +++ b/src/Handler/Shipment/ShipmentSenderHandler.php @@ -12,14 +12,13 @@ namespace Mollie\Handler\Shipment; +use Mollie\Api\Exceptions\ApiException; use Mollie\Api\MollieApiClient; use Mollie\Exception\ShipmentCannotBeSentException; -use Mollie\Service\ExceptionService; use Mollie\Service\Shipment\ShipmentInformationSenderInterface; use Mollie\Verification\Shipment\ShipmentVerificationInterface; use Order; use OrderState; -use Psr\Log\LoggerInterface; class ShipmentSenderHandler implements ShipmentSenderHandlerInterface { @@ -33,57 +32,24 @@ class ShipmentSenderHandler implements ShipmentSenderHandlerInterface */ private $shipmentInformationSender; - /** - * @var ExceptionService - */ - private $exceptionService; - - /** - * @var LoggerInterface - */ - private $moduleLogger; - public function __construct( ShipmentVerificationInterface $canSendShipment, - ShipmentInformationSenderInterface $shipmentInformationSender, - ExceptionService $exceptionService, - LoggerInterface $moduleLogger + ShipmentInformationSenderInterface $shipmentInformationSender ) { $this->canSendShipment = $canSendShipment; $this->shipmentInformationSender = $shipmentInformationSender; - $this->exceptionService = $exceptionService; - $this->moduleLogger = $moduleLogger; } /** - * @param MollieApiClient $apiClient - * @param Order $order - * @param OrderState $orderState - * - * @return bool + * @throws ShipmentCannotBeSentException + * @throws ApiException */ - public function handleShipmentSender(MollieApiClient $apiClient, Order $order, OrderState $orderState) + public function handleShipmentSender(?MollieApiClient $apiClient, Order $order, OrderState $orderState): void { - try { - if (!$this->canSendShipment->verify($order, $orderState)) { - return false; - } - $this->shipmentInformationSender->sendShipmentInformation($apiClient, $order); - } catch (ShipmentCannotBeSentException $exception) { - $message = $this->exceptionService->getErrorMessageForException( - $exception, - $this->exceptionService->getErrorMessages(), - ['orderReference' => $order->reference] - ); - $this->moduleLogger->error($message); - - return false; - } catch (\Exception $exception) { - $this->moduleLogger->error($exception->getMessage()); - - return false; + if (!$this->canSendShipment->verify($order, $orderState)) { + return; } - return true; + $this->shipmentInformationSender->sendShipmentInformation($apiClient, $order); } } diff --git a/src/Handler/Shipment/ShipmentSenderHandlerInterface.php b/src/Handler/Shipment/ShipmentSenderHandlerInterface.php index 9edd013ff..27e9412d6 100644 --- a/src/Handler/Shipment/ShipmentSenderHandlerInterface.php +++ b/src/Handler/Shipment/ShipmentSenderHandlerInterface.php @@ -12,16 +12,17 @@ namespace Mollie\Handler\Shipment; +use Mollie\Api\Exceptions\ApiException; use Mollie\Api\MollieApiClient; +use Mollie\Exception\ShipmentCannotBeSentException; use Order; use OrderState; interface ShipmentSenderHandlerInterface { /** - * @param MollieApiClient $apiClient - * @param Order $order - * @param OrderState $orderState + * @throws ShipmentCannotBeSentException + * @throws ApiException */ - public function handleShipmentSender(MollieApiClient $apiClient, Order $order, OrderState $orderState); + public function handleShipmentSender(?MollieApiClient $apiClient, Order $order, OrderState $orderState); } diff --git a/src/Install/DatabaseTableInstaller.php b/src/Install/DatabaseTableInstaller.php index 586f9b4cd..48ee1371b 100644 --- a/src/Install/DatabaseTableInstaller.php +++ b/src/Install/DatabaseTableInstaller.php @@ -69,7 +69,8 @@ private function getCommands() `minimal_order_value` decimal(20,6), `max_order_value` decimal(20,6), `surcharge` INT(10), - `surcharge_fixed_amount` decimal(20,6), + `surcharge_fixed_amount_tax_excl` decimal(20,6), + `tax_rules_group_id` INT(10), `surcharge_percentage` decimal(20,6), `surcharge_limit` decimal(20,6), `images_json` TEXT, @@ -86,10 +87,12 @@ private function getCommands() `issuers_json` TEXT NOT NULL ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;'; - $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'mol_order_fee` ( - `id_mol_order_fee` INT(64) NOT NULL PRIMARY KEY AUTO_INCREMENT, + $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'mol_order_payment_fee` ( + `id_mol_order_payment_fee` INT(64) NOT NULL PRIMARY KEY AUTO_INCREMENT, `id_cart` INT(64) NOT NULL, - `order_fee` decimal(20,6) NOT NULL + `id_order` INT(64) NOT NULL, + `fee_tax_incl` decimal(20,6) NOT NULL, + `fee_tax_excl` decimal(20,6) NOT NULL ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;'; $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'mol_carrier_information` ( diff --git a/src/Install/DatabaseTableUninstaller.php b/src/Install/DatabaseTableUninstaller.php index 256b2a2d6..7f6d24ed5 100644 --- a/src/Install/DatabaseTableUninstaller.php +++ b/src/Install/DatabaseTableUninstaller.php @@ -13,18 +13,9 @@ namespace Mollie\Install; use Db; -use Mollie\Factory\ModuleFactory; final class DatabaseTableUninstaller implements UninstallerInterface { - /** @var ModuleFactory */ - private $moduleFactory; - - public function __construct(ModuleFactory $moduleFactory) - { - $this->moduleFactory = $moduleFactory; - } - public function uninstall(): bool { foreach ($this->getCommands() as $query) { @@ -43,17 +34,13 @@ private function getCommands(): array $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_country`;'; $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_payment_method`;'; $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_payment_method_issuer`;'; - $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_order_fee`;'; + $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_order_payment_fee`;'; $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_carrier_information`;'; $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_pending_order_cart`;'; $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_excluded_country`;'; $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_pending_order_cart_rule`;'; $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'mol_payment_method_order_total_restriction`;'; - if ($moduleName = $this->moduleFactory->getModuleName()) { - $sql[] = 'UPDATE ' . _DB_PREFIX_ . 'order_state SET deleted = 1 WHERE module_name = "' . pSQL($moduleName) . '";'; - } - return $sql; } } diff --git a/src/Install/Installer.php b/src/Install/Installer.php index 8014a2e06..9455cc6ab 100644 --- a/src/Install/Installer.php +++ b/src/Install/Installer.php @@ -12,7 +12,6 @@ namespace Mollie\Install; -use Configuration; use Db; use DbQuery; use Exception; @@ -22,12 +21,12 @@ use Mollie; use Mollie\Adapter\ConfigurationAdapter; use Mollie\Config\Config; +use Mollie\Exception\CouldNotInstallModule; +use Mollie\Factory\ModuleFactory; use Mollie\Handler\ErrorHandler\ErrorHandler; -use Mollie\Service\OrderStateImageService; use Mollie\Tracker\Segment; use Mollie\Utility\MultiLangUtility; use OrderState; -use PrestaShopDatabaseException; use PrestaShopException; use Tab; use Tools; @@ -48,12 +47,7 @@ class Installer implements InstallerInterface private $module; /** - * @var OrderStateImageService - */ - private $imageService; - - /** - * @var InstallerInterface + * @var DatabaseTableInstaller */ private $databaseTableInstaller; @@ -66,25 +60,28 @@ class Installer implements InstallerInterface * @var ConfigurationAdapter */ private $configurationAdapter; + /** @var OrderStateInstaller */ + private $orderStateInstaller; public function __construct( - Mollie $module, - OrderStateImageService $imageService, - InstallerInterface $databaseTableInstaller, + ModuleFactory $moduleFactory, + DatabaseTableInstaller $databaseTableInstaller, Segment $segment, - ConfigurationAdapter $configurationAdapter + ConfigurationAdapter $configurationAdapter, + OrderStateInstaller $orderStateInstaller ) { - $this->module = $module; - $this->imageService = $imageService; + $this->module = $moduleFactory->getModule(); $this->databaseTableInstaller = $databaseTableInstaller; $this->segment = $segment; $this->configurationAdapter = $configurationAdapter; + $this->orderStateInstaller = $orderStateInstaller; } public function install() { $this->segment->setMessage('Mollie installed'); $this->segment->track(); + $errorHandler = ErrorHandler::getInstance(); foreach (self::getHooks() as $hook) { @@ -96,8 +93,8 @@ public function install() } try { - $this->createMollieStatuses(); - } catch (Exception $e) { + $this->orderStateInstaller->install(); + } catch (CouldNotInstallModule $e) { $errorHandler->handle($e, $e->getCode(), false); $this->errors[] = $this->module->l('Unable to install Mollie statuses', self::FILE_NAME); @@ -173,245 +170,6 @@ public static function getHooks() ]; } - /** - * @return bool - * - * @throws PrestaShopDatabaseException - * @throws PrestaShopException - */ - private function createPartialRefundOrderState() - { - if ($this->isStatusCreated(Config::MOLLIE_STATUS_PARTIAL_REFUND)) { - return true; - } - $orderState = new OrderState(); - $orderState->send_email = false; - $orderState->color = '#6F8C9F'; - $orderState->hidden = false; - $orderState->delivery = false; - $orderState->logable = false; - $orderState->invoice = false; - $orderState->module_name = $this->module->name; - $orderState->name = MultiLangUtility::createMultiLangField('Partially refunded by Mollie'); - if ($orderState->add()) { - $this->imageService->createOrderStateLogo($orderState->id); - } - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_PARTIAL_REFUND, (int) $orderState->id); - - return true; - } - - /** - * @return bool - * - * @throws PrestaShopDatabaseException - * @throws PrestaShopException - */ - public function createPartialShippedOrderState() - { - if ($this->isStatusCreated(Config::MOLLIE_STATUS_PARTIALLY_SHIPPED)) { - return true; - } - $orderState = new OrderState(); - $orderState->send_email = false; - $orderState->color = '#8A2BE2'; - $orderState->hidden = false; - $orderState->delivery = false; - $orderState->logable = false; - $orderState->invoice = false; - $orderState->module_name = $this->module->name; - $orderState->name = MultiLangUtility::createMultiLangField('Partially shipped'); - - if ($orderState->add()) { - $this->imageService->createOrderStateLogo($orderState->id); - } - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_PARTIALLY_SHIPPED, (int) $orderState->id); - - return true; - } - - public function createMollieStatuses() - { - if (!$this->createPartialRefundOrderState()) { - return false; - } - if (!$this->createAwaitingMollieOrderState()) { - return false; - } - if (!$this->createPartialShippedOrderState()) { - return false; - } - if (!$this->createOrderCompletedOrderState()) { - return false; - } - if (!$this->klarnaPaymentAuthorizedState()) { - return false; - } - if (!$this->klarnaPaymentShippedState()) { - return false; - } - if (!$this->createChargedbackState()) { - return false; - } - - return true; - } - - /** - * @return bool - * - * @throws PrestaShopDatabaseException - * @throws PrestaShopException - */ - public function createAwaitingMollieOrderState() - { - if ($this->isStatusCreated(Config::MOLLIE_STATUS_AWAITING)) { - return true; - } - $orderState = new OrderState(); - $orderState->send_email = false; - $orderState->color = '#4169E1'; - $orderState->hidden = false; - $orderState->delivery = false; - $orderState->logable = false; - $orderState->invoice = false; - $orderState->module_name = $this->module->name; - $orderState->name = MultiLangUtility::createMultiLangField('Awaiting Mollie payment'); - - if ($orderState->add()) { - $this->imageService->createOrderStateLogo($orderState->id); - } - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_AWAITING, (int) $orderState->id); - - return true; - } - - /** - * @return bool - * - * @throws PrestaShopDatabaseException - * @throws PrestaShopException - */ - public function createOrderCompletedOrderState() - { - if ($this->isStatusCreated(Config::MOLLIE_STATUS_ORDER_COMPLETED)) { - return true; - } - $orderState = new OrderState(); - $orderState->send_email = false; - $orderState->color = '#3d7d1c'; - $orderState->hidden = false; - $orderState->delivery = false; - $orderState->logable = false; - $orderState->invoice = false; - $orderState->send_email = true; - $orderState->module_name = $this->module->name; - $orderState->name = MultiLangUtility::createMultiLangField('Completed'); - - if ($orderState->add()) { - $this->imageService->createOrderStateLogo($orderState->id); - } - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_ORDER_COMPLETED, (int) $orderState->id); - - return true; - } - - /** - * @return bool - * - * @throws PrestaShopDatabaseException - * @throws PrestaShopException - */ - public function klarnaPaymentAuthorizedState() - { - if ($this->isStatusCreated(Config::MOLLIE_STATUS_KLARNA_AUTHORIZED)) { - return true; - } - $orderState = new OrderState(); - $orderState->send_email = true; - $orderState->color = '#8A2BE2'; - $orderState->hidden = false; - $orderState->delivery = false; - $orderState->logable = true; - $orderState->invoice = true; - $orderState->pdf_invoice = true; - $orderState->paid = true; - $orderState->send_email = true; - $orderState->template = 'payment'; - $orderState->module_name = $this->module->name; - $orderState->name = MultiLangUtility::createMultiLangField('Klarna payment authorized'); - - if ($orderState->add()) { - $this->imageService->createOrderStateLogo($orderState->id); - } - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_KLARNA_AUTHORIZED, (int) $orderState->id); - $this->configurationAdapter->updateValue(Config::MOLLIE_KLARNA_INVOICE_ON, Config::MOLLIE_STATUS_KLARNA_AUTHORIZED); - - return true; - } - - /** - * @return bool - * - * @throws PrestaShopDatabaseException - * @throws PrestaShopException - */ - public function klarnaPaymentShippedState() - { - if ($this->isStatusCreated(Config::MOLLIE_STATUS_KLARNA_SHIPPED)) { - return true; - } - $orderState = new OrderState(); - $orderState->send_email = true; - $orderState->color = '#8A2BE2'; - $orderState->hidden = false; - $orderState->delivery = false; - $orderState->logable = true; - $orderState->invoice = false; - $orderState->shipped = true; - $orderState->paid = true; - $orderState->delivery = true; - $orderState->template = 'shipped'; - $orderState->pdf_invoice = true; - $orderState->module_name = $this->module->name; - $orderState->name = MultiLangUtility::createMultiLangField('Klarna payment shipped'); - - if ($orderState->add()) { - $this->imageService->createOrderStateLogo($orderState->id); - } - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_KLARNA_SHIPPED, (int) $orderState->id); - - return true; - } - - /** - * @return bool - * - * @throws PrestaShopDatabaseException - * @throws PrestaShopException - */ - public function createChargedbackState() - { - if ($this->isStatusCreated(Config::MOLLIE_STATUS_CHARGEBACK)) { - return true; - } - $orderState = new OrderState(); - $orderState->send_email = false; - $orderState->color = '#E74C3C'; - $orderState->hidden = false; - $orderState->delivery = false; - $orderState->logable = false; - $orderState->invoice = false; - $orderState->module_name = $this->module->name; - $orderState->name = MultiLangUtility::createMultiLangField('Mollie Chargeback'); - if ($orderState->add()) { - $this->imageService->createOrderStateLogo($orderState->id); - } - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_CHARGEBACK, (int) $orderState->id); - - return true; - } - /** * @return void */ @@ -432,11 +190,11 @@ protected function initConfig() $this->configurationAdapter->updateValue(Config::MOLLIE_METHOD_COUNTRIES_DISPLAY, 0); $this->configurationAdapter->updateValue(Config::MOLLIE_DISPLAY_ERRORS, false); $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_OPEN, $this->configurationAdapter->get(Config::MOLLIE_STATUS_AWAITING)); - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_PAID, Configuration::get('PS_OS_PAYMENT')); + $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_PAID, $this->configurationAdapter->get('PS_OS_PAYMENT')); $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_COMPLETED, $this->configurationAdapter->get(Config::MOLLIE_STATUS_ORDER_COMPLETED)); - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_CANCELED, Configuration::get('PS_OS_CANCELED')); - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_EXPIRED, Configuration::get('PS_OS_CANCELED')); - $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_REFUNDED, Configuration::get('PS_OS_REFUND')); + $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_CANCELED, $this->configurationAdapter->get('PS_OS_CANCELED')); + $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_EXPIRED, $this->configurationAdapter->get('PS_OS_CANCELED')); + $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_REFUNDED, $this->configurationAdapter->get('PS_OS_REFUND')); $this->configurationAdapter->updateValue(Config::MOLLIE_STATUS_SHIPPING, $this->configurationAdapter->get(Config::MOLLIE_STATUS_PARTIALLY_SHIPPED)); $this->configurationAdapter->updateValue(Config::MOLLIE_MAIL_WHEN_SHIPPING, true); $this->configurationAdapter->updateValue(Config::MOLLIE_MAIL_WHEN_PAID, true); @@ -523,7 +281,7 @@ public function copyEmailTemplates() public function installVoucherFeatures() { - $mollieVoucherId = Configuration::get(Config::MOLLIE_VOUCHER_FEATURE_ID); + $mollieVoucherId = $this->configurationAdapter->get(Config::MOLLIE_VOUCHER_FEATURE_ID); if ($mollieVoucherId) { $mollieFeature = new Feature((int) $mollieVoucherId); $doesFeatureExist = Validate::isLoadedObject($mollieFeature); @@ -546,27 +304,4 @@ public function installVoucherFeatures() $this->configurationAdapter->updateValue(Config::MOLLIE_VOUCHER_FEATURE_ID, $feature->id); } - - private function isStatusCreated($statusName) - { - $status = new OrderState((int) $this->configurationAdapter->get($statusName)); - if (Validate::isLoadedObject($status)) { - $this->enableStatus($status); - - return true; - } - - return false; - } - - /** - * @param OrderState $orderState - */ - private function enableStatus($orderState) - { - if ((bool) $orderState->deleted) { - $orderState->deleted = false; - $orderState->save(); - } - } } diff --git a/src/Install/OrderStateInstaller.php b/src/Install/OrderStateInstaller.php new file mode 100644 index 000000000..9a07dcac7 --- /dev/null +++ b/src/Install/OrderStateInstaller.php @@ -0,0 +1,193 @@ +moduleFactory = $moduleFactory; + $this->configurationAdapter = $configurationAdapter; + $this->orderStateImageService = $orderStateImageService; + } + + /** + * @throws CouldNotInstallModule + */ + public function install() + { + $this->installOrderState( + Config::MOLLIE_STATUS_PARTIAL_REFUND, + new OrderStateData( + 'Partially refunded by Mollie', + '#6F8C9F' + ) + ); + + $this->installOrderState( + Config::MOLLIE_STATUS_AWAITING, + new OrderStateData( + 'Awaiting Mollie payment', + '#4169E1' + ) + ); + + $this->installOrderState( + Config::MOLLIE_STATUS_PARTIALLY_SHIPPED, + new OrderStateData( + 'Partially shipped', + '#8A2BE2' + ) + ); + + $this->installOrderState( + Config::MOLLIE_STATUS_ORDER_COMPLETED, + new OrderStateData( + 'Completed', + '#3d7d1c', + true + ) + ); + + $this->installOrderState( + Config::MOLLIE_STATUS_KLARNA_AUTHORIZED, + new OrderStateData( + 'Klarna payment authorized', + '#8A2BE2', + true, + true, + false, + true, + false, + true, + EmailTemplate::PAYMENT, + true + ) + ); + + $this->installOrderState( + Config::MOLLIE_STATUS_KLARNA_SHIPPED, + new OrderStateData( + 'Klarna payment shipped', + '#8A2BE2', + true, + true, + true, + false, + true, + true, + EmailTemplate::SHIPPED, + true + ) + ); + + $this->installOrderState( + Config::MOLLIE_STATUS_CHARGEBACK, + new OrderStateData( + 'Mollie Chargeback', + '#E74C3C' + ) + ); + + return true; + } + + /** + * @throws CouldNotInstallModule + */ + private function installOrderState(string $orderStatus, OrderStateData $orderStateInstallerData) + { + if ($this->validateIfStatusExists($orderStatus)) { + $this->enableState($orderStatus); + + return; + } + + $orderState = $this->createOrderState($orderStateInstallerData); + + $this->updateStateConfiguration($orderStatus, $orderState); + } + + private function validateIfStatusExists(string $key): bool + { + $existingStateId = (int) $this->configurationAdapter->get($key); + $orderState = new OrderState($existingStateId); + + // if state already existed we won't install new one. + return \Validate::isLoadedObject($orderState); + } + + private function enableState(string $key) + { + $existingStateId = (int) $this->configurationAdapter->get($key); + $orderState = new OrderState($existingStateId); + + if ((bool) !$orderState->deleted) { + return; + } + + $orderState->deleted = false; + $orderState->save(); + } + + /** + * @throws CouldNotInstallModule + */ + private function createOrderState(OrderStateData $orderStateInstallerData): OrderState + { + $orderState = new OrderState(); + + $orderState->send_email = $orderStateInstallerData->isSendEmail(); + $orderState->color = $orderStateInstallerData->getColor(); + $orderState->delivery = $orderStateInstallerData->isDelivery(); + $orderState->logable = $orderStateInstallerData->isLogable(); + $orderState->invoice = $orderStateInstallerData->isInvoice(); + $orderState->module_name = $this->moduleFactory->getModuleName(); + $orderState->shipped = $orderStateInstallerData->isShipped(); + $orderState->paid = $orderStateInstallerData->isPaid(); + $orderState->template = $orderStateInstallerData->getTemplate(); + $orderState->pdf_invoice = $orderStateInstallerData->isPdfInvoice(); + $orderState->hidden = false; + $orderState->unremovable = false; + + $languages = \Language::getLanguages(); + + foreach ($languages as $language) { + $orderState->name[$language['id_lang']] = $orderStateInstallerData->getName(); + } + + try { + $orderState->add(); + } catch (\Exception $exception) { + throw CouldNotInstallModule::failedToInstallOrderState($orderStateInstallerData->getName(), $exception); + } + + $this->orderStateImageService->createOrderStateLogo($orderState->id); + + return $orderState; + } + + private function updateStateConfiguration(string $key, OrderState $orderState) + { + $this->configurationAdapter->updateValue($key, (int) $orderState->id); + } +} diff --git a/src/Install/Uninstall.php b/src/Install/Uninstall.php index 526dd4274..a6f7c1fff 100644 --- a/src/Install/Uninstall.php +++ b/src/Install/Uninstall.php @@ -12,7 +12,7 @@ namespace Mollie\Install; -use Configuration; +use Mollie\Adapter\ConfigurationAdapter; use Mollie\Config\Config; use Mollie\Tracker\Segment; use Tab; @@ -32,13 +32,16 @@ class Uninstall * @var Segment */ private $segment; + private $configurationAdapter; public function __construct( UninstallerInterface $databaseUninstaller, - Segment $segment + Segment $segment, + ConfigurationAdapter $configurationAdapter ) { $this->databaseUninstaller = $databaseUninstaller; $this->segment = $segment; + $this->configurationAdapter = $configurationAdapter; } public function uninstall() @@ -91,33 +94,13 @@ private function deleteConfig() Config::MOLLIE_API_KEY_TEST, ]; - $orderStateConfigurations = [ - Config::MOLLIE_STATUS_PARTIAL_REFUND, - Config::MOLLIE_STATUS_AWAITING, - Config::MOLLIE_STATUS_PARTIALLY_SHIPPED, - Config::MOLLIE_STATUS_ORDER_COMPLETED, - Config::MOLLIE_STATUS_KLARNA_AUTHORIZED, - Config::MOLLIE_STATUS_KLARNA_SHIPPED, - Config::MOLLIE_STATUS_CHARGEBACK, - Config::MOLLIE_STATUS_OPEN, - Config::MOLLIE_STATUS_PAID, - Config::MOLLIE_STATUS_COMPLETED, - Config::MOLLIE_STATUS_CANCELED, - Config::MOLLIE_STATUS_EXPIRED, - Config::MOLLIE_STATUS_REFUNDED, - Config::MOLLIE_STATUS_SHIPPING, - Config::MOLLIE_STATUS_DEFAULT, - ]; - - $this->deleteConfigurations( - array_merge($configurations, $orderStateConfigurations) - ); + $this->deleteConfigurations($configurations); } private function deleteConfigurations(array $configurations) { foreach ($configurations as $configuration) { - Configuration::deleteByName($configuration); + $this->configurationAdapter->delete($configuration); } } diff --git a/src/Logger/PrestaLogger.php b/src/Logger/PrestaLogger.php index 8890ceeac..67afcc3c8 100644 --- a/src/Logger/PrestaLogger.php +++ b/src/Logger/PrestaLogger.php @@ -13,9 +13,8 @@ namespace Mollie\Logger; use Mollie\Exception\NotImplementedException; -use Psr\Log\LoggerInterface; -class PrestaLogger implements LoggerInterface +class PrestaLogger implements PrestaLoggerInterface { public function emergency($message, array $context = []) { diff --git a/src/Logger/PrestaLoggerInterface.php b/src/Logger/PrestaLoggerInterface.php new file mode 100644 index 000000000..5d55bd00f --- /dev/null +++ b/src/Logger/PrestaLoggerInterface.php @@ -0,0 +1,9 @@ +context->getCart()->getOrderTotal(); } diff --git a/src/Provider/OrderTotal/OrderTotalProviderInterface.php b/src/Provider/OrderTotal/OrderTotalProviderInterface.php index 47a2e7220..00a0a1f5c 100644 --- a/src/Provider/OrderTotal/OrderTotalProviderInterface.php +++ b/src/Provider/OrderTotal/OrderTotalProviderInterface.php @@ -34,9 +34,9 @@ * @codingStandardsIgnoreStart */ -namespace Mollie\Provider; +namespace Mollie\Provider\OrderTotal; interface OrderTotalProviderInterface { - public function getOrderTotal(); + public function getOrderTotal(): float; } diff --git a/src/Provider/PaymentFeeProvider.php b/src/Provider/PaymentFeeProvider.php index ffd254313..4daad5fda 100644 --- a/src/Provider/PaymentFeeProvider.php +++ b/src/Provider/PaymentFeeProvider.php @@ -36,23 +36,84 @@ namespace Mollie\Provider; -use Mollie\Utility\PaymentFeeUtility; +use Address; +use Mollie\Adapter\Context; +use Mollie\Calculator\PaymentFeeCalculator; +use Mollie\Config\Config; +use Mollie\DTO\PaymentFeeData; +use Mollie\Exception\Code\ExceptionCode; +use Mollie\Exception\FailedToProvidePaymentFeeException; +use Mollie\Repository\AddressRepositoryInterface; use MolPaymentMethod; class PaymentFeeProvider implements PaymentFeeProviderInterface { - /** - * @var OrderTotalProviderInterface - */ - private $orderTotalProvider; + /** @var Context */ + private $context; + /** @var AddressRepositoryInterface */ + private $addressRepository; + /** @var TaxCalculatorProvider */ + private $taxProvider; - public function __construct(OrderTotalProviderInterface $orderTotalProvider) - { - $this->orderTotalProvider = $orderTotalProvider; + public function __construct( + Context $context, + AddressRepositoryInterface $addressRepository, + TaxCalculatorProvider $taxProvider + ) { + $this->context = $context; + $this->addressRepository = $addressRepository; + $this->taxProvider = $taxProvider; } - public function getPaymentFee(MolPaymentMethod $paymentMethod) + /** + * {@inheritDoc} + */ + public function getPaymentFee(MolPaymentMethod $paymentMethod, float $totalCartPriceTaxIncl): PaymentFeeData { - return PaymentFeeUtility::getPaymentFee($paymentMethod, $this->orderTotalProvider->getOrderTotal()); + // TODO handle exception on all calls. + $surchargeFixedPriceTaxExcl = $paymentMethod->surcharge_fixed_amount_tax_excl; + $surchargePercentage = (float) $paymentMethod->surcharge_percentage; + $surchargeLimit = (float) $paymentMethod->surcharge_limit; + + /** @var Address|null $address */ + $address = $this->addressRepository->findOneBy([ + 'id_address' => $this->context->getCustomerAddressInvoiceId(), + 'deleted' => 0, + ]); + + if (!$address || !$address->id) { + throw new FailedToProvidePaymentFeeException('Failed to find customer address', ExceptionCode::FAILED_TO_FIND_CUSTOMER_ADDRESS); + } + + $taxCalculator = $this->taxProvider->getTaxCalculator( + $paymentMethod->tax_rules_group_id, + $address->id_country, + $address->id_state + ); + + $paymentFeeCalculator = new PaymentFeeCalculator($taxCalculator, $this->context); + + // TODO it would be good to use Abstract class, which would hold common private methods and then create separate services, which would provide calculated fee. + switch ($paymentMethod->surcharge) { + case Config::FEE_FIXED_FEE: + return $paymentFeeCalculator->calculateFixedFee( + $surchargeFixedPriceTaxExcl + ); + case Config::FEE_PERCENTAGE: + return $paymentFeeCalculator->calculatePercentageFee( + $totalCartPriceTaxIncl, + $surchargePercentage, + $surchargeLimit + ); + case Config::FEE_FIXED_FEE_AND_PERCENTAGE: + return $paymentFeeCalculator->calculatePercentageAndFixedPriceFee( + $totalCartPriceTaxIncl, + $surchargePercentage, + $surchargeFixedPriceTaxExcl, + $surchargeLimit + ); + } + + return new PaymentFeeData(0.00, 0.00, 0.00, false); } } diff --git a/src/Provider/PaymentFeeProviderInterface.php b/src/Provider/PaymentFeeProviderInterface.php index 5a8224c10..756795c6f 100644 --- a/src/Provider/PaymentFeeProviderInterface.php +++ b/src/Provider/PaymentFeeProviderInterface.php @@ -36,9 +36,14 @@ namespace Mollie\Provider; +use Mollie\DTO\PaymentFeeData; +use Mollie\Exception\FailedToProvidePaymentFeeException; use MolPaymentMethod; interface PaymentFeeProviderInterface { - public function getPaymentFee(MolPaymentMethod $paymentMethod); + /** + * @throws FailedToProvidePaymentFeeException + */ + public function getPaymentFee(MolPaymentMethod $paymentMethod, float $totalCartPriceTaxIncl): PaymentFeeData; } diff --git a/src/Provider/PaymentOption/BancontactPaymentOptionProvider.php b/src/Provider/PaymentOption/BancontactPaymentOptionProvider.php index 9078eb4d6..a8ea23fdd 100644 --- a/src/Provider/PaymentOption/BancontactPaymentOptionProvider.php +++ b/src/Provider/PaymentOption/BancontactPaymentOptionProvider.php @@ -40,6 +40,7 @@ use Mollie\Adapter\LegacyContext; use Mollie\Api\Types\PaymentMethod; use Mollie\Provider\CreditCardLogoProvider; +use Mollie\Provider\OrderTotal\OrderTotalProviderInterface; use Mollie\Provider\PaymentFeeProviderInterface; use Mollie\Service\LanguageService; use MolPaymentMethod; @@ -74,27 +75,32 @@ class BancontactPaymentOptionProvider implements PaymentOptionProviderInterface * @var LanguageService */ private $languageService; + /** @var OrderTotalProviderInterface */ + private $orderTotalProvider; public function __construct( Mollie $module, LegacyContext $context, CreditCardLogoProvider $creditCardLogoProvider, PaymentFeeProviderInterface $paymentFeeProvider, - LanguageService $languageService + LanguageService $languageService, + OrderTotalProviderInterface $orderTotalProvider ) { $this->module = $module; $this->context = $context; $this->creditCardLogoProvider = $creditCardLogoProvider; $this->paymentFeeProvider = $paymentFeeProvider; $this->languageService = $languageService; + $this->orderTotalProvider = $orderTotalProvider; } /** * {@inheritDoc} */ - public function getPaymentOption(MolPaymentMethod $paymentMethod) + public function getPaymentOption(MolPaymentMethod $paymentMethod): PaymentOption { $paymentOption = new PaymentOption(); + $paymentOption->setCallToActionText( $paymentMethod->title ?: $this->languageService->lang($paymentMethod->method_name) @@ -107,7 +113,8 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) true )); $paymentOption->setLogo($this->creditCardLogoProvider->getMethodOptionLogo($paymentMethod)); - $paymentFee = $this->paymentFeeProvider->getPaymentFee($paymentMethod); + + $paymentFeeData = $this->paymentFeeProvider->getPaymentFee($paymentMethod, $this->orderTotalProvider->getOrderTotal()); $this->context->getSmarty()->assign([ 'methodId' => $paymentMethod->getPaymentMethodName(), @@ -126,24 +133,33 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) ], ] ); - if ($paymentFee) { + + if ($paymentFeeData->isActive()) { $paymentOption->setInputs( [ [ 'type' => 'hidden', 'name' => 'payment-fee-price', - 'value' => $paymentFee, + 'value' => $paymentFeeData->getPaymentFeeTaxIncl(), ], [ 'type' => 'hidden', 'name' => 'payment-fee-price-display', - 'value' => sprintf($this->module->l('Payment Fee: %1s', self::FILE_NAME), Tools::displayPrice($paymentFee)), + 'value' => sprintf( + $this->module->l('Payment Fee: %1s', self::FILE_NAME), + Tools::displayPrice($paymentFeeData->getPaymentFeeTaxIncl()) + ), ], [ 'type' => 'hidden', 'name' => 'mollie-method-id', 'value' => PaymentMethod::BANCONTACT, ], + [ + 'type' => 'hidden', + 'name' => 'payment-method-id', + 'value' => $paymentMethod->id, + ], ] ); } diff --git a/src/Provider/PaymentOption/BasePaymentOptionProvider.php b/src/Provider/PaymentOption/BasePaymentOptionProvider.php index d4b8f154b..33580b119 100644 --- a/src/Provider/PaymentOption/BasePaymentOptionProvider.php +++ b/src/Provider/PaymentOption/BasePaymentOptionProvider.php @@ -39,6 +39,7 @@ use Mollie; use Mollie\Adapter\LegacyContext; use Mollie\Provider\CreditCardLogoProvider; +use Mollie\Provider\OrderTotal\OrderTotalProviderInterface; use Mollie\Provider\PaymentFeeProviderInterface; use Mollie\Service\LanguageService; use MolPaymentMethod; @@ -73,27 +74,32 @@ class BasePaymentOptionProvider implements PaymentOptionProviderInterface * @var LanguageService */ private $languageService; + /** @var OrderTotalProviderInterface */ + private $orderTotalProvider; public function __construct( Mollie $module, LegacyContext $context, CreditCardLogoProvider $creditCardLogoProvider, PaymentFeeProviderInterface $paymentFeeProvider, - LanguageService $languageService + LanguageService $languageService, + OrderTotalProviderInterface $orderTotalProvider ) { $this->module = $module; $this->context = $context; $this->creditCardLogoProvider = $creditCardLogoProvider; $this->paymentFeeProvider = $paymentFeeProvider; $this->languageService = $languageService; + $this->orderTotalProvider = $orderTotalProvider; } /** * {@inheritDoc} */ - public function getPaymentOption(MolPaymentMethod $paymentMethod) + public function getPaymentOption(MolPaymentMethod $paymentMethod): PaymentOption { $paymentOption = new PaymentOption(); + $paymentOption->setCallToActionText( $paymentMethod->title ?: $this->languageService->lang($paymentMethod->method_name) @@ -106,20 +112,29 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) true )); $paymentOption->setLogo($this->creditCardLogoProvider->getMethodOptionLogo($paymentMethod)); - $paymentFee = $this->paymentFeeProvider->getPaymentFee($paymentMethod); - if ($paymentFee) { + $paymentFeeData = $this->paymentFeeProvider->getPaymentFee($paymentMethod, $this->orderTotalProvider->getOrderTotal()); + + if ($paymentFeeData->isActive()) { $paymentOption->setInputs( [ [ 'type' => 'hidden', 'name' => 'payment-fee-price', - 'value' => $paymentFee, + 'value' => $paymentFeeData->getPaymentFeeTaxIncl(), ], [ 'type' => 'hidden', 'name' => 'payment-fee-price-display', - 'value' => sprintf($this->module->l('Payment Fee: %1s', self::FILE_NAME), Tools::displayPrice($paymentFee)), + 'value' => sprintf( + $this->module->l('Payment Fee: %1s', self::FILE_NAME), + Tools::displayPrice($paymentFeeData->getPaymentFeeTaxIncl()) + ), + ], + [ + 'type' => 'hidden', + 'name' => 'payment-method-id', + 'value' => $paymentMethod->id, ], ] ); diff --git a/src/Provider/PaymentOption/CreditCardPaymentOptionProvider.php b/src/Provider/PaymentOption/CreditCardPaymentOptionProvider.php index 2cb0c96f9..cfe183703 100644 --- a/src/Provider/PaymentOption/CreditCardPaymentOptionProvider.php +++ b/src/Provider/PaymentOption/CreditCardPaymentOptionProvider.php @@ -36,14 +36,14 @@ namespace Mollie\Provider\PaymentOption; -use Configuration; use MolCustomer; use Mollie; +use Mollie\Adapter\ConfigurationAdapter; use Mollie\Adapter\Customer; use Mollie\Adapter\LegacyContext; use Mollie\Config\Config; use Mollie\Provider\CreditCardLogoProvider; -use Mollie\Provider\OrderTotalProviderInterface; +use Mollie\Provider\OrderTotal\OrderTotalProviderInterface; use Mollie\Provider\PaymentFeeProviderInterface; use Mollie\Repository\MolCustomerRepository; use Mollie\Service\LanguageService; @@ -93,6 +93,8 @@ class CreditCardPaymentOptionProvider implements PaymentOptionProviderInterface * @var MolCustomerRepository */ private $customerRepository; + /** @var ConfigurationAdapter */ + private $configurationAdapter; public function __construct( LegacyContext $context, @@ -102,7 +104,8 @@ public function __construct( LanguageService $languageService, Customer $customer, MolCustomerRepository $customerRepository, - Mollie $module + Mollie $module, + ConfigurationAdapter $configurationAdapter ) { $this->context = $context; $this->creditCardLogoProvider = $creditCardLogoProvider; @@ -112,12 +115,13 @@ public function __construct( $this->customer = $customer; $this->customerRepository = $customerRepository; $this->module = $module; + $this->configurationAdapter = $configurationAdapter; } /** * {@inheritDoc} */ - public function getPaymentOption(MolPaymentMethod $paymentMethod) + public function getPaymentOption(MolPaymentMethod $paymentMethod): PaymentOption { $paymentOption = new PaymentOption(); $paymentOption->setCallToActionText( @@ -141,7 +145,7 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) ] ); - $useSavedUser = (bool) (Configuration::get(Config::MOLLIE_SINGLE_CLICK_PAYMENT) && $molCustomer); + $useSavedUser = (bool) ($this->configurationAdapter->get(Config::MOLLIE_SINGLE_CLICK_PAYMENT) && $molCustomer); $paymentOption->setInputs([ [ @@ -171,7 +175,7 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) 'price' => $this->orderTotalProvider->getOrderTotal(), 'priceSign' => $this->context->getCurrencySign(), 'methodId' => $paymentMethod->getPaymentMethodName(), - 'isSingleClickPayment' => (bool) Configuration::get(Mollie\Config\Config::MOLLIE_SINGLE_CLICK_PAYMENT), + 'isSingleClickPayment' => (bool) (int) $this->configurationAdapter->get(Mollie\Config\Config::MOLLIE_SINGLE_CLICK_PAYMENT), 'mollieUseSavedCard' => $useSavedUser, 'isGuest' => $this->customer->getCustomer()->isGuest(), ]); @@ -181,20 +185,28 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) $this->module->getPathUri(), 'views/templates/hook/mollie_iframe.tpl' )); - $paymentFee = $this->paymentFeeProvider->getPaymentFee($paymentMethod); + $paymentFeeData = $this->paymentFeeProvider->getPaymentFee($paymentMethod, $this->orderTotalProvider->getOrderTotal()); - if ($paymentFee) { + if ($paymentFeeData->isActive()) { $paymentOption->setInputs( array_merge($paymentOption->getInputs(), [ [ 'type' => 'hidden', 'name' => 'payment-fee-price', - 'value' => $paymentFee, + 'value' => $paymentFeeData->getPaymentFeeTaxIncl(), ], [ 'type' => 'hidden', 'name' => 'payment-fee-price-display', - 'value' => sprintf($this->module->l('Payment Fee: %1s', self::FILE_NAME), Tools::displayPrice($paymentFee)), + 'value' => sprintf( + $this->module->l('Payment Fee: %1s', self::FILE_NAME), + Tools::displayPrice($paymentFeeData->getPaymentFeeTaxIncl()) + ), + ], + [ + 'type' => 'hidden', + 'name' => 'payment-method-id', + 'value' => $paymentMethod->id, ], ]) ); diff --git a/src/Provider/PaymentOption/CreditCardSingleClickPaymentOptionProvider.php b/src/Provider/PaymentOption/CreditCardSingleClickPaymentOptionProvider.php index b5c5ce1fd..4739aa2d1 100644 --- a/src/Provider/PaymentOption/CreditCardSingleClickPaymentOptionProvider.php +++ b/src/Provider/PaymentOption/CreditCardSingleClickPaymentOptionProvider.php @@ -36,13 +36,13 @@ namespace Mollie\Provider\PaymentOption; -use Configuration; use MolCustomer; use Mollie; +use Mollie\Adapter\ConfigurationAdapter; use Mollie\Adapter\LegacyContext; use Mollie\Config\Config; use Mollie\Provider\CreditCardLogoProvider; -use Mollie\Provider\OrderTotalProviderInterface; +use Mollie\Provider\OrderTotal\OrderTotalProviderInterface; use Mollie\Provider\PaymentFeeProviderInterface; use Mollie\Repository\MolCustomerRepository; use Mollie\Service\LanguageService; @@ -90,6 +90,8 @@ class CreditCardSingleClickPaymentOptionProvider implements PaymentOptionProvide private $customerRepository; /** @var Mollie\Adapter\Customer */ private $customer; + /** @var ConfigurationAdapter */ + private $configurationAdapter; public function __construct( Mollie $module, @@ -99,7 +101,8 @@ public function __construct( PaymentFeeProviderInterface $paymentFeeProvider, LanguageService $languageService, MolCustomerRepository $customerRepository, - Mollie\Adapter\Customer $customer + Mollie\Adapter\Customer $customer, + ConfigurationAdapter $configurationAdapter ) { $this->module = $module; $this->context = $context; @@ -109,14 +112,16 @@ public function __construct( $this->languageService = $languageService; $this->customerRepository = $customerRepository; $this->customer = $customer; + $this->configurationAdapter = $configurationAdapter; } /** * {@inheritDoc} */ - public function getPaymentOption(MolPaymentMethod $paymentMethod) + public function getPaymentOption(MolPaymentMethod $paymentMethod): PaymentOption { $paymentOption = new PaymentOption(); + $paymentOption->setCallToActionText( $paymentMethod->title ?: $this->languageService->lang($paymentMethod->method_name) @@ -138,7 +143,7 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) ] ); - $useSavedUser = (bool) (Configuration::get(Config::MOLLIE_SINGLE_CLICK_PAYMENT) && $molCustomer); + $useSavedUser = (bool) ($this->configurationAdapter->get(Config::MOLLIE_SINGLE_CLICK_PAYMENT) && $molCustomer); $paymentOption->setInputs([ [ @@ -167,7 +172,7 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) 'price' => $this->orderTotalProvider->getOrderTotal(), 'priceSign' => $this->context->getCurrencySign(), 'methodId' => $paymentMethod->getPaymentMethodName(), - 'isSingleClickPayment' => (bool) Configuration::get(Mollie\Config\Config::MOLLIE_SINGLE_CLICK_PAYMENT), + 'isSingleClickPayment' => (bool) (int) $this->configurationAdapter->get(Mollie\Config\Config::MOLLIE_SINGLE_CLICK_PAYMENT), 'mollieUseSavedCard' => $useSavedUser, 'isGuest' => $this->customer->getCustomer()->isGuest(), ]); @@ -177,20 +182,28 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) $this->module->getPathUri(), 'views/templates/hook/mollie_single_click.tpl' )); - $paymentFee = $this->paymentFeeProvider->getPaymentFee($paymentMethod); + $paymentFeeData = $this->paymentFeeProvider->getPaymentFee($paymentMethod, $this->orderTotalProvider->getOrderTotal()); - if ($paymentFee) { + if ($paymentFeeData->isActive()) { $paymentOption->setInputs( array_merge($paymentOption->getInputs(), [ [ 'type' => 'hidden', 'name' => 'payment-fee-price', - 'value' => $paymentFee, + 'value' => $paymentFeeData->getPaymentFeeTaxIncl(), ], [ 'type' => 'hidden', 'name' => 'payment-fee-price-display', - 'value' => sprintf($this->module->l('Payment Fee: %1s', self::FILE_NAME), Tools::displayPrice($paymentFee)), + 'value' => sprintf( + $this->module->l('Payment Fee: %1s', self::FILE_NAME), + Tools::displayPrice($paymentFeeData->getPaymentFeeTaxIncl()) + ), + ], + [ + 'type' => 'hidden', + 'name' => 'payment-method-id', + 'value' => $paymentMethod->id, ], ]) ); diff --git a/src/Provider/PaymentOption/IdealPaymentOptionProvider.php b/src/Provider/PaymentOption/IdealPaymentOptionProvider.php index 077f7bce6..d089700d8 100644 --- a/src/Provider/PaymentOption/IdealPaymentOptionProvider.php +++ b/src/Provider/PaymentOption/IdealPaymentOptionProvider.php @@ -40,6 +40,7 @@ use Mollie\Adapter\LegacyContext; use Mollie\Builder\Content\PaymentOption\IdealDropdownInfoBlock; use Mollie\Provider\CreditCardLogoProvider; +use Mollie\Provider\OrderTotal\OrderTotalProviderInterface; use Mollie\Provider\PaymentFeeProviderInterface; use Mollie\Service\Content\TemplateParserInterface; use Mollie\Service\LanguageService; @@ -85,6 +86,8 @@ class IdealPaymentOptionProvider implements PaymentOptionProviderInterface * @var LanguageService */ private $languageService; + /** @var OrderTotalProviderInterface */ + private $orderTotalProvider; public function __construct( Mollie $module, @@ -93,7 +96,8 @@ public function __construct( PaymentFeeProviderInterface $paymentFeeProvider, TemplateParserInterface $templateParser, IdealDropdownInfoBlock $idealDropdownInfoBlock, - LanguageService $languageService + LanguageService $languageService, + OrderTotalProviderInterface $orderTotalProvider ) { $this->module = $module; $this->context = $context; @@ -102,14 +106,16 @@ public function __construct( $this->templateParser = $templateParser; $this->idealDropdownInfoBlock = $idealDropdownInfoBlock; $this->languageService = $languageService; + $this->orderTotalProvider = $orderTotalProvider; } /** * {@inheritDoc} */ - public function getPaymentOption(MolPaymentMethod $paymentMethod) + public function getPaymentOption(MolPaymentMethod $paymentMethod): PaymentOption { $paymentOption = new PaymentOption(); + $paymentOption->setCallToActionText( $paymentMethod->title ?: $this->languageService->lang($paymentMethod->method_name) @@ -136,9 +142,9 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) )); $paymentOption->setLogo($this->creditCardLogoProvider->getMethodOptionLogo($paymentMethod)); - $paymentFee = $this->paymentFeeProvider->getPaymentFee($paymentMethod); + $paymentFeeData = $this->paymentFeeProvider->getPaymentFee($paymentMethod, $this->orderTotalProvider->getOrderTotal()); - if ($paymentFee) { + if ($paymentFeeData->isActive()) { $paymentOption->setInputs( [ [ @@ -149,12 +155,20 @@ public function getPaymentOption(MolPaymentMethod $paymentMethod) [ 'type' => 'hidden', 'name' => 'payment-fee-price', - 'value' => $paymentFee, + 'value' => $paymentFeeData->getPaymentFeeTaxIncl(), ], [ 'type' => 'hidden', 'name' => 'payment-fee-price-display', - 'value' => sprintf($this->module->l('Payment Fee: %1s', self::FILE_NAME), Tools::displayPrice($paymentFee)), + 'value' => sprintf( + $this->module->l('Payment Fee: %1s', self::FILE_NAME), + Tools::displayPrice($paymentFeeData->getPaymentFeeTaxIncl()) + ), + ], + [ + 'type' => 'hidden', + 'name' => 'payment-method-id', + 'value' => $paymentMethod->id, ], ] ); diff --git a/src/Provider/PaymentOption/PaymentOptionProviderInterface.php b/src/Provider/PaymentOption/PaymentOptionProviderInterface.php index 6636c6245..8d463f0c8 100644 --- a/src/Provider/PaymentOption/PaymentOptionProviderInterface.php +++ b/src/Provider/PaymentOption/PaymentOptionProviderInterface.php @@ -46,5 +46,5 @@ interface PaymentOptionProviderInterface * * @return PaymentOption */ - public function getPaymentOption(MolPaymentMethod $paymentMethod); + public function getPaymentOption(MolPaymentMethod $paymentMethod): PaymentOption; } diff --git a/src/Provider/TaxCalculatorProvider.php b/src/Provider/TaxCalculatorProvider.php new file mode 100644 index 000000000..d26388972 --- /dev/null +++ b/src/Provider/TaxCalculatorProvider.php @@ -0,0 +1,69 @@ +taxRuleRepository = $taxRuleRepository; + $this->taxRepository = $taxRepository; + } + + /** + * @param int $taxRulesGroupId + * @param int $countryId + * @param int $stateId + * + * @return TaxCalculator + */ + public function getTaxCalculator(int $taxRulesGroupId, int $countryId, int $stateId): TaxCalculator + { + $taxRules = $this->taxRuleRepository->getTaxRule( + $taxRulesGroupId, + $countryId, + $stateId + ); + + $taxes = []; + $behavior = 0; + $firstRow = true; + + foreach ($taxRules as $taxRule) { + /** @var Tax|null $tax */ + $tax = $this->taxRepository->findOneBy([ + 'id_tax' => $taxRule['id_tax'], + ]); + + if (!$tax || !$tax->id) { + continue; + } + + $taxes[] = $tax; + + // the applied behavior correspond to the most specific rules + if ($firstRow) { + $behavior = (int) $taxRule['behavior']; + $firstRow = false; + } + + if ((int) $taxRule['behavior'] === 0) { + break; + } + } + + return new TaxCalculator($taxes, $behavior); + } +} diff --git a/src/Repository/AddressRepository.php b/src/Repository/AddressRepository.php new file mode 100644 index 000000000..867c90f2d --- /dev/null +++ b/src/Repository/AddressRepository.php @@ -0,0 +1,13 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * + * @category Mollie + * + * @see https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Repository; + +class GenderRepository extends AbstractRepository implements GenderRepositoryInterface +{ + public function __construct() + { + parent::__construct(\Gender::class); + } +} diff --git a/src/Repository/GenderRepositoryInterface.php b/src/Repository/GenderRepositoryInterface.php new file mode 100644 index 000000000..f98a21a00 --- /dev/null +++ b/src/Repository/GenderRepositoryInterface.php @@ -0,0 +1,41 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * + * @category Mollie + * + * @see https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Repository; + +interface GenderRepositoryInterface extends \Mollie\Repository\ReadOnlyRepositoryInterface +{ +} diff --git a/src/Repository/OrderFeeRepository.php b/src/Repository/MolOrderPaymentFeeRepository.php similarity index 56% rename from src/Repository/OrderFeeRepository.php rename to src/Repository/MolOrderPaymentFeeRepository.php index f277f6235..b9694d710 100644 --- a/src/Repository/OrderFeeRepository.php +++ b/src/Repository/MolOrderPaymentFeeRepository.php @@ -12,14 +12,12 @@ namespace Mollie\Repository; -use Db; +use MolOrderPaymentFee; -class OrderFeeRepository +class MolOrderPaymentFeeRepository extends AbstractRepository implements MolOrderPaymentFeeRepositoryInterface { - public function getOrderFeeIdByCartId($cartId) + public function __construct() { - $sql = 'Select id_mol_order_fee FROM `' . _DB_PREFIX_ . 'mol_order_fee` WHERE id_cart = "' . (int) $cartId . '"'; - - return Db::getInstance()->getValue($sql); + parent::__construct(MolOrderPaymentFee::class); } } diff --git a/src/Repository/MolOrderPaymentFeeRepositoryInterface.php b/src/Repository/MolOrderPaymentFeeRepositoryInterface.php new file mode 100644 index 000000000..390e2a0d7 --- /dev/null +++ b/src/Repository/MolOrderPaymentFeeRepositoryInterface.php @@ -0,0 +1,7 @@ +select('tr.id_tax, tr.behavior') + ->from('tax_rule', 'tr') + ->where('tr.id_tax_rules_group = ' . $taxRulesGroupId) + ->where('tr.id_country = ' . $countryId) + ->where('tr.id_state IN (0, ' . $stateId . ')') + ->orderBy('tr.id_state DESC'); + + $result = Db::getInstance((bool) _PS_USE_SQL_SLAVE_)->executeS($query); + + if (empty($result)) { + return []; + } + + return $result; + } +} diff --git a/src/Repository/TaxRuleRepositoryInterface.php b/src/Repository/TaxRuleRepositoryInterface.php new file mode 100644 index 000000000..34338c709 --- /dev/null +++ b/src/Repository/TaxRuleRepositoryInterface.php @@ -0,0 +1,8 @@ +select('trg.id_tax_rules_group as id, trg.name') + ->from('tax_rules_group', 'trg') + ->leftJoin('tax_rules_group_shop', 'trgs', 'trgs.id_tax_rules_group = trg.id_tax_rules_group') + ->where('trg.deleted = 0') + ->where('trg.active = 1') + ->where('trgs.id_shop = ' . $shopId); + + $result = Db::getInstance((bool) _PS_USE_SQL_SLAVE_)->executeS($query); + + if (empty($result)) { + return []; + } + + return $result; + } +} diff --git a/src/Repository/TaxRulesGroupRepositoryInterface.php b/src/Repository/TaxRulesGroupRepositoryInterface.php new file mode 100644 index 000000000..f88334d94 --- /dev/null +++ b/src/Repository/TaxRulesGroupRepositoryInterface.php @@ -0,0 +1,8 @@ +countryRepository = $countryRepository; $this->paymentMethodSortProvider = $paymentMethodSortProvider; @@ -87,6 +94,8 @@ public function __construct( $this->environment = (int) $this->configurationAdapter->get(Config::MOLLIE_ENVIRONMENT); $this->transactionService = $transactionService; $this->shop = $shop; + $this->taxProvider = $taxProvider; + $this->context = $context; } /** @@ -137,13 +146,20 @@ public function getMethodsForConfig(MollieApiClient $api) 'issuers' => $apiMethod->issuers, 'tipEnableSSL' => $tipEnableSSL, 'minimumAmount' => $apiMethod->minimumAmount ? [ - 'value' => $apiMethod->minimumAmount->value, + 'value' => NumberUtility::toPrecision( + $apiMethod->minimumAmount->value, + NumberUtility::FLOAT_PRECISION + ), 'currency' => $apiMethod->minimumAmount->currency, ] : false, 'maximumAmount' => $apiMethod->maximumAmount ? [ - 'value' => $apiMethod->maximumAmount->value, + 'value' => NumberUtility::toPrecision( + $apiMethod->maximumAmount->value, + NumberUtility::FLOAT_PRECISION + ), 'currency' => $apiMethod->maximumAmount->currency, ] : false, + 'surcharge_fixed_amount_tax_incl' => 0, ]; } @@ -175,21 +191,48 @@ private function getMethodsObjForConfig($apiMethods) $emptyPaymentMethod->minimal_order_value = ''; $emptyPaymentMethod->max_order_value = ''; $emptyPaymentMethod->surcharge = 0; - $emptyPaymentMethod->surcharge_fixed_amount = ''; + $emptyPaymentMethod->surcharge_fixed_amount_tax_excl = 0; + $emptyPaymentMethod->tax_rules_group_id = 0; $emptyPaymentMethod->surcharge_percentage = ''; $emptyPaymentMethod->surcharge_limit = ''; foreach ($apiMethods as $apiMethod) { $paymentId = $this->methodRepository->getPaymentMethodIdByMethodId($apiMethod['id'], $this->environment); + if ($paymentId) { $paymentMethod = new MolPaymentMethod((int) $paymentId); + + $paymentMethod = $this->toPrecisionForDecimalNumbers($paymentMethod); + + if (!empty($paymentMethod->surcharge_fixed_amount_tax_excl)) { + $apiMethod['surcharge_fixed_amount_tax_incl'] = $this->getSurchargeFixedAmountTaxInclPrice( + $paymentMethod->surcharge_fixed_amount_tax_excl, + $paymentMethod->tax_rules_group_id, + $this->context->getCountryId() + ); + + $paymentMethod->surcharge_fixed_amount_tax_excl = NumberUtility::toPrecision( + $paymentMethod->surcharge_fixed_amount_tax_excl, + NumberUtility::FLOAT_PRECISION + ); + + $apiMethod['surcharge_fixed_amount_tax_incl'] = NumberUtility::toPrecision( + $apiMethod['surcharge_fixed_amount_tax_incl'], + NumberUtility::FLOAT_PRECISION + ); + } + $methods[$apiMethod['id']] = $apiMethod; $methods[$apiMethod['id']]['obj'] = $paymentMethod; + continue; } + $defaultPaymentMethod = clone $emptyPaymentMethod; + $defaultPaymentMethod->id_method = $apiMethod['id']; $defaultPaymentMethod->method_name = $apiMethod['name']; + $methods[$apiMethod['id']] = $apiMethod; $methods[$apiMethod['id']]['obj'] = $defaultPaymentMethod; } @@ -372,4 +415,43 @@ public function requestApplePayPaymentSession(?MollieApiClient $api, string $val return $api->wallets->requestApplePayPaymentSession($this->shop->getShop()->domain, $validationUrl); } + + private function getSurchargeFixedAmountTaxInclPrice(float $priceTaxExcl, int $taxRulesGroupId, int $countryId): float + { + $taxCalculator = $this->taxProvider->getTaxCalculator( + $taxRulesGroupId, + $countryId, + 0 // NOTE: there is no default state for back office so setting no state + ); + + return NumberUtility::toPrecision( + $taxCalculator->addTaxes($priceTaxExcl), + NumberUtility::FLOAT_PRECISION + ); + } + + private function toPrecisionForDecimalNumbers(MolPaymentMethod $paymentMethod): MolPaymentMethod + { + $paymentMethod->surcharge_percentage = (string) NumberUtility::toPrecision( + (float) $paymentMethod->surcharge_percentage, + NumberUtility::FLOAT_PRECISION + ); + + $paymentMethod->surcharge_limit = (string) NumberUtility::toPrecision( + (float) $paymentMethod->surcharge_limit, + NumberUtility::FLOAT_PRECISION + ); + + $paymentMethod->min_amount = NumberUtility::toPrecision( + $paymentMethod->min_amount, + NumberUtility::FLOAT_PRECISION + ); + + $paymentMethod->max_amount = NumberUtility::toPrecision( + $paymentMethod->max_amount, + NumberUtility::FLOAT_PRECISION + ); + + return $paymentMethod; + } } diff --git a/src/Service/CartLinesService.php b/src/Service/CartLinesService.php index 5c605b6ef..5ca553cc8 100644 --- a/src/Service/CartLinesService.php +++ b/src/Service/CartLinesService.php @@ -13,10 +13,12 @@ namespace Mollie\Service; use Cart; +use Mollie\Adapter\Context; use Mollie\Adapter\ToolsAdapter; use Mollie\Config\Config; use Mollie\DTO\Line; use Mollie\DTO\Object\Amount; +use Mollie\DTO\PaymentFeeData; use Mollie\Utility\CalculationUtility; use Mollie\Utility\CartPriceUtility; use Mollie\Utility\NumberUtility; @@ -38,17 +40,19 @@ class CartLinesService * @var ToolsAdapter */ private $tools; + private $context; - public function __construct(LanguageService $languageService, VoucherService $voucherService, ToolsAdapter $tools) + public function __construct(LanguageService $languageService, VoucherService $voucherService, ToolsAdapter $tools, Context $context) { $this->voucherService = $voucherService; $this->languageService = $languageService; $this->tools = $tools; + $this->context = $context; } /** * @param float $amount - * @param float $paymentFee + * @param PaymentFeeData $paymentFeeData * @param string $currencyIsoCode * @param array $cartSummary * @param float $shippingCost @@ -62,7 +66,7 @@ public function __construct(LanguageService $languageService, VoucherService $vo */ public function getCartLines( $amount, - $paymentFee, + $paymentFeeData, $currencyIsoCode, $cartSummary, $shippingCost, @@ -70,6 +74,8 @@ public function getCartLines( $psGiftWrapping, $selectedVoucherCategory ) { + // TODO refactor whole service, split order line append into separate services and test them individually at least!!! + $apiRoundingPrecision = Config::API_ROUNDING_PRECISION; $vatRatePrecision = Config::VAT_RATE_ROUNDING_PRECISION; @@ -108,7 +114,7 @@ public function getCartLines( $orderLines = $this->addWrappingLine($wrappingPrice, $cartSummary, $vatRatePrecision, $apiRoundingPrecision, $orderLines); // Add fee - $orderLines = $this->addPaymentFeeLine($paymentFee, $apiRoundingPrecision, $orderLines); + $orderLines = $this->addPaymentFeeLine($paymentFeeData, $apiRoundingPrecision, $orderLines); // Ungroup all the cart lines, just one level $newItems = $this->ungroupLines($orderLines); @@ -194,6 +200,8 @@ private function createProductLines(array $cartItems, $apiRoundingPrecision, $gi 'unitPrice' => 0, 'totalAmount' => 0, 'category' => '', + 'product_url' => $this->context->getProductLink($cartItem['id_product']), + 'image_url' => $this->context->getImageLink($cartItem['link_rewrite'], $cartItem['id_image']), ]; continue; } @@ -212,6 +220,8 @@ private function createProductLines(array $cartItems, $apiRoundingPrecision, $gi 'unitPrice' => round($cartItem['price_wt'], $apiRoundingPrecision), 'totalAmount' => (float) $roundedTotalWithTax, 'category' => $this->voucherService->getVoucherCategory($cartItem, $selectedVoucherCategory), + 'product_url' => $this->context->getProductLink($cartItem['id_product']), + 'image_url' => $this->context->getImageLink($cartItem['link_rewrite'], $cartItem['id_image']), ]; $remaining -= $roundedTotalWithTax; } @@ -334,6 +344,8 @@ private function fillProductLinesWithRemainingData(array $orderLines, $apiRoundi 'totalAmount' => round($totalAmount, $apiRoundingPrecision), 'vatRate' => round($actualVatRate, $apiRoundingPrecision), 'vatAmount' => round($vatAmount, $apiRoundingPrecision), + 'product_url' => $line['product_url'] ?? null, + 'image_url' => $line['image_url'] ?? null, ]; if (isset($line['sku'])) { $newItem['sku'] = $line['sku']; @@ -410,28 +422,30 @@ private function addWrappingLine($wrappingPrice, array $cartSummary, $vatRatePre } /** - * @param float $paymentFee + * @param PaymentFeeData $paymentFeeData * @param int $apiRoundingPrecision * @param array $orderLines * * @return array */ - private function addPaymentFeeLine($paymentFee, $apiRoundingPrecision, array $orderLines) + private function addPaymentFeeLine($paymentFeeData, $apiRoundingPrecision, array $orderLines) { - if ($paymentFee) { - $orderLines['surcharge'] = [ - [ - 'name' => $this->languageService->lang('Payment fee'), - 'sku' => Config::PAYMENT_FEE_SKU, - 'quantity' => 1, - 'unitPrice' => round($paymentFee, $apiRoundingPrecision), - 'totalAmount' => round($paymentFee, $apiRoundingPrecision), - 'vatAmount' => 0, - 'vatRate' => 0, - ], - ]; + if (!$paymentFeeData->isActive()) { + return $orderLines; } + $orderLines['surcharge'] = [ + [ + 'name' => $this->languageService->lang('Payment fee'), + 'sku' => Config::PAYMENT_FEE_SKU, + 'quantity' => 1, + 'unitPrice' => round($paymentFeeData->getPaymentFeeTaxIncl(), $apiRoundingPrecision), + 'totalAmount' => round($paymentFeeData->getPaymentFeeTaxIncl(), $apiRoundingPrecision), + 'vatAmount' => NumberUtility::minus($paymentFeeData->getPaymentFeeTaxIncl(), $paymentFeeData->getPaymentFeeTaxExcl()), + 'vatRate' => $paymentFeeData->getTaxRate(), + ], + ]; + return $orderLines; } @@ -497,6 +511,8 @@ private function convertToLineArray(array $newItems, $currencyIsoCode, $apiRound } $line->setVatRate(TextFormatUtility::formatNumber($item['vatRate'], $apiRoundingPrecision, '.', '')); + $line->setProductUrl($item['product_url'] ?? null); + $line->setImageUrl($item['image_url'] ?? null); $newItems[$index] = $line; } diff --git a/src/Service/ConfigFieldService.php b/src/Service/ConfigFieldService.php index 32f3bac55..1b7b7e385 100644 --- a/src/Service/ConfigFieldService.php +++ b/src/Service/ConfigFieldService.php @@ -31,15 +31,19 @@ class ConfigFieldService * @var CountryRepository */ private $countryRepository; + /** @var Mollie\Adapter\ConfigurationAdapter */ + private $configurationAdapter; public function __construct( Mollie $module, ApiService $apiService, - CountryRepository $countryRepository + CountryRepository $countryRepository, + Mollie\Adapter\ConfigurationAdapter $configurationAdapter ) { $this->module = $module; $this->apiService = $apiService; $this->countryRepository = $countryRepository; + $this->configurationAdapter = $configurationAdapter; } /** @@ -53,13 +57,13 @@ public function getConfigFieldsValues() Config::MOLLIE_API_KEY_TEST => Configuration::get(Config::MOLLIE_API_KEY_TEST), Config::MOLLIE_PAYMENTSCREEN_LOCALE => Configuration::get(Config::MOLLIE_PAYMENTSCREEN_LOCALE), Config::MOLLIE_SEND_ORDER_CONFIRMATION => Configuration::get(Config::MOLLIE_SEND_ORDER_CONFIRMATION), - Config::MOLLIE_IFRAME => Configuration::get(Config::MOLLIE_IFRAME), - Config::MOLLIE_SINGLE_CLICK_PAYMENT => Configuration::get(Config::MOLLIE_SINGLE_CLICK_PAYMENT), + Config::MOLLIE_IFRAME[(int) $this->configurationAdapter->get(Config::MOLLIE_ENVIRONMENT) ? 'production' : 'sandbox'] => $this->configurationAdapter->get(Config::MOLLIE_IFRAME), + Config::MOLLIE_SINGLE_CLICK_PAYMENT[(int) $this->configurationAdapter->get(Config::MOLLIE_ENVIRONMENT) ? 'production' : 'sandbox'] => $this->configurationAdapter->get(Config::MOLLIE_SINGLE_CLICK_PAYMENT), Config::MOLLIE_CSS => Configuration::get(Config::MOLLIE_CSS), Config::MOLLIE_IMAGES => Configuration::get(Config::MOLLIE_IMAGES), Config::MOLLIE_SHOW_RESEND_PAYMENT_LINK => Configuration::get(Config::MOLLIE_SHOW_RESEND_PAYMENT_LINK), - Config::MOLLIE_ISSUERS => Configuration::get(Config::MOLLIE_ISSUERS), + Config::MOLLIE_ISSUERS[(int) $this->configurationAdapter->get(Config::MOLLIE_ENVIRONMENT) ? 'production' : 'sandbox'] => $this->configurationAdapter->get(Config::MOLLIE_ISSUERS), Config::MOLLIE_METHOD_COUNTRIES => Configuration::get(Config::MOLLIE_METHOD_COUNTRIES), Config::MOLLIE_METHOD_COUNTRIES_DISPLAY => Configuration::get(Config::MOLLIE_METHOD_COUNTRIES_DISPLAY), diff --git a/src/Service/ExceptionService.php b/src/Service/ExceptionService.php index 68dd2486c..2c1acedf8 100644 --- a/src/Service/ExceptionService.php +++ b/src/Service/ExceptionService.php @@ -30,7 +30,7 @@ public function __construct(Mollie $module) $this->module = $module; } - public function getErrorMessages() + public function getErrorMessages(): array { return [ OrderCreationException::class => [ @@ -42,15 +42,18 @@ public function getErrorMessages() ], ShipmentCannotBeSentException::class => [ ShipmentCannotBeSentException::NO_SHIPPING_INFORMATION => $this->module->l('Shipment information cannot be sent. Order reference (%s) has no shipping information.', self::FILE_NAME), - ShipmentCannotBeSentException::AUTOMATIC_SHIPMENT_SENDER_IS_NOT_AVAILABLE => $this->module->l('Shipment information cannot be sent. Order reference (%s) does not have automatic shipment sender available.', self::FILE_NAME), ShipmentCannotBeSentException::ORDER_HAS_NO_PAYMENT_INFORMATION => $this->module->l('Shipment information cannot be sent. Order reference (%s) has no payment information.', self::FILE_NAME), ShipmentCannotBeSentException::PAYMENT_IS_NOT_ORDER => $this->module->l('Shipment information cannot be sent. Order reference (%s) is a regular payment.', self::FILE_NAME), ], ]; } - public function getErrorMessageForException(Exception $exception, array $messages, array $params = []) + public function getErrorMessageForException(Exception $exception, array $messages = [], array $params = []) { + if (empty($messages)) { + $messages = $this->getErrorMessages(); + } + $exceptionType = get_class($exception); $exceptionCode = $exception->getCode(); diff --git a/src/Service/OrderFeeService.php b/src/Service/OrderPaymentFeeService.php similarity index 54% rename from src/Service/OrderFeeService.php rename to src/Service/OrderPaymentFeeService.php index 1b6d4228f..e518c4e05 100644 --- a/src/Service/OrderFeeService.php +++ b/src/Service/OrderPaymentFeeService.php @@ -14,15 +14,15 @@ use Configuration; use Mollie\Config\Config; +use Mollie\DTO\PaymentFeeData; +use Mollie\Provider\PaymentFeeProviderInterface; use Mollie\Repository\PaymentMethodRepositoryInterface; -use Mollie\Utility\PaymentFeeUtility; -use MolOrderFee; +use MolOrderPaymentFee; use MolPaymentMethod; use PrestaShopException; use Shop; -use Tools; -class OrderFeeService +class OrderPaymentFeeService { /** * @var PaymentMethodRepositoryInterface @@ -32,50 +32,47 @@ class OrderFeeService * @var Shop */ private $shop; + /** @var PaymentFeeProviderInterface */ + private $paymentFeeProvider; - public function __construct(PaymentMethodRepositoryInterface $paymentMethodRepository, Shop $shop) - { + public function __construct( + PaymentMethodRepositoryInterface $paymentMethodRepository, + Shop $shop, + PaymentFeeProviderInterface $paymentFeeProvider + ) { $this->paymentMethodRepository = $paymentMethodRepository; $this->shop = $shop; + $this->paymentFeeProvider = $paymentFeeProvider; } - public function getPaymentFees($methods, $totalPrice) + public function createOrderPaymentFee(int $orderId, int $cartId, PaymentFeeData $paymentFeeData): void { - foreach ($methods as $index => $method) { - if (0 === (int) $method['surcharge']) { - $methods[$index]['fee'] = false; - $methods[$index]['fee_display'] = false; - continue; - } - $paymentMethod = new MolPaymentMethod($method['id_payment_method']); - $paymentFee = PaymentFeeUtility::getPaymentFee($paymentMethod, $totalPrice); - $methods[$index]['fee'] = $paymentFee; - $methods[$index]['fee_display'] = Tools::displayPrice($paymentFee); - } + $molOrderPaymentFee = new MolOrderPaymentFee(); - return $methods; - } + $molOrderPaymentFee->id_cart = $cartId; + $molOrderPaymentFee->id_order = $orderId; + $molOrderPaymentFee->fee_tax_incl = $paymentFeeData->getPaymentFeeTaxIncl(); + $molOrderPaymentFee->fee_tax_excl = $paymentFeeData->getPaymentFeeTaxExcl(); - public function createOrderFee($cartId, $orderFee) - { - $orderFeeObj = new MolOrderFee(); - $orderFeeObj->id_cart = (int) $cartId; - $orderFeeObj->order_fee = $orderFee; try { - $orderFeeObj->add(); + $molOrderPaymentFee->add(); } catch (\Exception $e) { $errorHandler = \Mollie\Handler\ErrorHandler\ErrorHandler::getInstance(); $errorHandler->handle($e, $e->getCode(), false); + + // TODO use custom exceptions throw new PrestaShopException('Can\'t save Order fee'); } } - public function getPaymentFee(float $totalAmount, string $method): float + public function getPaymentFee(float $totalAmount, string $method): PaymentFeeData { + // TODO order and payment fee in same service? Separate logic as this is probably used in cart context + $environment = Configuration::get(Config::MOLLIE_ENVIRONMENT); $paymentId = $this->paymentMethodRepository->getPaymentMethodIdByMethodId($method, $environment, $this->shop->id); $molPaymentMethod = new MolPaymentMethod($paymentId); - return (float) PaymentFeeUtility::getPaymentFee($molPaymentMethod, $totalAmount); + return $this->paymentFeeProvider->getPaymentFee($molPaymentMethod, $totalAmount); } } diff --git a/src/Service/PaymentMethodService.php b/src/Service/PaymentMethodService.php index dee55608c..576d07e6f 100644 --- a/src/Service/PaymentMethodService.php +++ b/src/Service/PaymentMethodService.php @@ -15,13 +15,14 @@ use Address; use Cart; use Configuration; -use Context; use Country; use Currency; use Customer; use MolCustomer; use Mollie; use Mollie\Adapter\CartAdapter; +use Mollie\Adapter\ConfigurationAdapter; +use Mollie\Adapter\Context; use Mollie\Adapter\Shop; use Mollie\Api\Resources\BaseCollection; use Mollie\Api\Resources\MethodCollection; @@ -33,7 +34,10 @@ use Mollie\DTO\PaymentData; use Mollie\Exception\OrderCreationException; use Mollie\Provider\CreditCardLogoProvider; +use Mollie\Provider\OrderTotal\OrderTotalProviderInterface; +use Mollie\Provider\PaymentFeeProviderInterface; use Mollie\Provider\PhoneNumberProviderInterface; +use Mollie\Repository\GenderRepositoryInterface; use Mollie\Repository\PaymentMethodRepositoryInterface; use Mollie\Service\PaymentMethod\PaymentMethodRestrictionValidationInterface; use Mollie\Service\PaymentMethod\PaymentMethodSortProviderInterface; @@ -41,7 +45,6 @@ use Mollie\Utility\CustomLogoUtility; use Mollie\Utility\EnvironmentUtility; use Mollie\Utility\LocaleUtility; -use Mollie\Utility\PaymentFeeUtility; use Mollie\Utility\SecureKeyUtility; use Mollie\Utility\TextFormatUtility; use MolPaymentMethod; @@ -98,6 +101,15 @@ class PaymentMethodService private $subscriptionOrder; /** @var CartAdapter */ private $cartAdapter; + /** @var ConfigurationAdapter */ + private $configurationAdapter; + private $genderRepository; + /** @var PaymentFeeProviderInterface */ + private $paymentFeeProvider; + /** @var Context */ + private $context; + /** @var OrderTotalProviderInterface */ + private $orderTotalProvider; public function __construct( Mollie $module, @@ -111,7 +123,12 @@ public function __construct( PaymentMethodRestrictionValidationInterface $paymentMethodRestrictionValidation, Shop $shop, SubscriptionOrderValidator $subscriptionOrder, - CartAdapter $cartAdapter + CartAdapter $cartAdapter, + ConfigurationAdapter $configurationAdapter, + GenderRepositoryInterface $genderRepository, + PaymentFeeProviderInterface $paymentFeeProvider, + Context $context, + OrderTotalProviderInterface $orderTotalProvider ) { $this->module = $module; $this->methodRepository = $methodRepository; @@ -125,6 +142,11 @@ public function __construct( $this->shop = $shop; $this->subscriptionOrder = $subscriptionOrder; $this->cartAdapter = $cartAdapter; + $this->configurationAdapter = $configurationAdapter; + $this->genderRepository = $genderRepository; + $this->paymentFeeProvider = $paymentFeeProvider; + $this->context = $context; + $this->orderTotalProvider = $orderTotalProvider; } public function savePaymentMethod($method) @@ -146,7 +168,8 @@ public function savePaymentMethod($method) $paymentMethod->minimal_order_value = Tools::getValue(Mollie\Config\Config::MOLLIE_METHOD_MINIMUM_ORDER_VALUE . $method['id']); $paymentMethod->max_order_value = Tools::getValue(Mollie\Config\Config::MOLLIE_METHOD_MAX_ORDER_VALUE . $method['id']); $paymentMethod->surcharge = Tools::getValue(Mollie\Config\Config::MOLLIE_METHOD_SURCHARGE_TYPE . $method['id']); - $paymentMethod->surcharge_fixed_amount = Tools::getValue(Mollie\Config\Config::MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT . $method['id']); + $paymentMethod->surcharge_fixed_amount_tax_excl = Tools::getValue(Mollie\Config\Config::MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT_TAX_EXCL . $method['id']); + $paymentMethod->tax_rules_group_id = Tools::getValue(Mollie\Config\Config::MOLLIE_METHOD_TAX_RULES_GROUP_ID . $method['id']); $paymentMethod->surcharge_percentage = Tools::getValue(Mollie\Config\Config::MOLLIE_METHOD_SURCHARGE_PERCENTAGE . $method['id']); $paymentMethod->surcharge_limit = Tools::getValue(Mollie\Config\Config::MOLLIE_METHOD_SURCHARGE_LIMIT . $method['id']); $paymentMethod->images_json = json_encode($method['image']); @@ -253,12 +276,14 @@ public function getPaymentData( string $applePayToken = '' ) { $totalAmount = TextFormatUtility::formatNumber($amount, 2); - $context = Context::getContext(); $cart = new Cart($cartId); $customer = new Customer($cart->id_customer); - $paymentFee = PaymentFeeUtility::getPaymentFee($molPaymentMethod, $totalAmount); - $totalAmount += $paymentFee; + $paymentFeeData = $this->paymentFeeProvider->getPaymentFee($molPaymentMethod, $totalAmount); + + if ($paymentFeeData->isActive()) { + $totalAmount += $paymentFeeData->getPaymentFeeTaxIncl(); + } $currency = (string) ($currency ? Tools::strtoupper($currency) : 'EUR'); $value = (float) TextFormatUtility::formatNumber($totalAmount, 2); @@ -269,7 +294,7 @@ public function getPaymentData( $cartId, $this->module->name ); - $redirectUrl = $context->link->getModuleLink( + $redirectUrl = $this->context->getModuleLink( 'mollie', 'return', [ @@ -283,7 +308,7 @@ public function getPaymentData( true ); - $webhookUrl = $context->link->getModuleLink( + $webhookUrl = $this->context->getModuleLink( 'mollie', 'webhook', [], @@ -329,7 +354,13 @@ public function getPaymentData( if ($this->subscriptionOrder->validate(new Cart($cartId))) { $molCustomer = $this->getCustomerInfo($cart->id_customer, true, false); - $paymentData->setCustomerId($molCustomer->customer_id); + + // TODO handle this - throw exception or add log message. + $paymentData->setCustomerId(''); + + if ($molCustomer) { + $paymentData->setCustomerId($molCustomer->customer_id); + } $paymentData->setSequenceType(SequenceType::SEQUENCETYPE_FIRST); } @@ -366,15 +397,27 @@ public function getPaymentData( $orderData->setOrderNumber($orderReference); $orderData->setLocale($this->getLocale($molPaymentMethod->method)); $orderData->setEmail($customer->email); + + /** @var \Gender|null $gender */ + $gender = $this->genderRepository->findOneBy(['id_gender' => $customer->id_gender]); + + if (!empty($gender) && isset($gender->name[$cart->id_lang])) { + $orderData->setTitle((string) $gender->name[$cart->id_lang]); + } + $orderData->setMethod($molPaymentMethod->id_method); $orderData->setMetadata($metaData); + if (!empty($customer->birthday) && $customer->birthday !== '0000-00-00') { + $orderData->setConsumerDateOfBirth((string) $customer->birthday); + } + $currency = new Currency($cart->id_currency); $selectedVoucherCategory = Configuration::get(Config::MOLLIE_VOUCHER_CATEGORY); $orderData->setLines( $this->cartLinesService->getCartLines( $amount, - $paymentFee, + $paymentFeeData, $currency->iso_code, $cart->getSummaryDetails(), $cart->getTotalShippingCost(null, true), @@ -386,7 +429,7 @@ public function getPaymentData( if ($cardToken) { $payment['cardToken'] = $cardToken; } - $payment['webhookUrl'] = $context->link->getModuleLink( + $payment['webhookUrl'] = $this->context->getModuleLink( 'mollie', 'webhook', [], @@ -412,6 +455,8 @@ public function getPaymentData( return $orderData; } + + // TODO handle no return option - throw exception } private function getLocale($method) @@ -457,14 +502,10 @@ private function removeNotSupportedMethods($methods, $mollieMethods) private function getSupportedMollieMethods(?string $sequenceType = null): array { - $context = Context::getContext(); - $addressId = $context->cart->id_address_invoice; - $address = new Address($addressId); + $address = new Address($this->context->getAddressInvoiceId()); $country = new Country($address->id_country); - $currency = $context->currency; - $language = $context->language; - $cartAmount = $context->cart->getOrderTotal(); + $cartAmount = $this->orderTotalProvider->getOrderTotal(); /** @var BaseCollection|MethodCollection $methods */ $methods = $this->module->getApiClient()->methods->allActive( @@ -472,11 +513,11 @@ private function getSupportedMollieMethods(?string $sequenceType = null): array 'resource' => 'orders', 'include' => 'issuers', 'includeWallets' => 'applepay', - 'locale' => $language->locale, + 'locale' => $this->context->getLanguageLocale(), 'billingCountry' => $country->iso_code, 'amount' => [ 'value' => (string) TextFormatUtility::formatNumber($cartAmount, 2), - 'currency' => $currency->iso_code, + 'currency' => $this->context->getCurrencyIso(), ], 'sequenceType' => $sequenceType, ] @@ -490,7 +531,7 @@ private function getSupportedMollieMethods(?string $sequenceType = null): array */ public function handleCustomerInfo(int $customerId, bool $saveCard, bool $useSavedCard): ?MolCustomer { - $isSingleClickPaymentEnabled = (bool) Configuration::get(Config::MOLLIE_SINGLE_CLICK_PAYMENT); + $isSingleClickPaymentEnabled = (bool) (int) $this->configurationAdapter->get(Config::MOLLIE_SINGLE_CLICK_PAYMENT); if (!$this->isCustomerSaveEnabled($isSingleClickPaymentEnabled)) { return null; } diff --git a/src/Service/SettingsSaveService.php b/src/Service/SettingsSaveService.php index a6d6321ac..2482bc790 100644 --- a/src/Service/SettingsSaveService.php +++ b/src/Service/SettingsSaveService.php @@ -17,6 +17,7 @@ use Context; use Exception; use Mollie; +use Mollie\Adapter\ConfigurationAdapter; use Mollie\Adapter\Shop; use Mollie\Api\Exceptions\ApiException; use Mollie\Api\Types\PaymentStatus; @@ -85,6 +86,7 @@ class SettingsSaveService /** @var Shop */ private $shop; + private $configurationAdapter; public function __construct( Mollie $module, @@ -96,7 +98,8 @@ public function __construct( PaymentMethodPositionHandlerInterface $paymentMethodPositionHandler, ApiKeyService $apiKeyService, CertificateHandlerInterface $applePayDirectCertificateHandler, - Shop $shop + Shop $shop, + ConfigurationAdapter $configurationAdapter ) { $this->module = $module; $this->countryRepository = $countryRepository; @@ -108,6 +111,7 @@ public function __construct( $this->apiService = $apiService; $this->applePayDirectCertificateHandler = $applePayDirectCertificateHandler; $this->shop = $shop; + $this->configurationAdapter = $configurationAdapter; } /** @@ -224,11 +228,11 @@ public function saveSettings(&$errors = []) } $molliePaymentscreenLocale = Tools::getValue(Config::MOLLIE_PAYMENTSCREEN_LOCALE); $mollieOrderConfirmationSand = Tools::getValue(Config::MOLLIE_SEND_ORDER_CONFIRMATION); - $mollieIFrameEnabled = Tools::getValue(Config::MOLLIE_IFRAME); - $mollieSingleClickPaymentEnabled = Tools::getValue(Config::MOLLIE_SINGLE_CLICK_PAYMENT); + $mollieIFrameEnabled = Tools::getValue(Config::MOLLIE_IFRAME[$environment ? 'production' : 'sandbox']); + $mollieSingleClickPaymentEnabled = Tools::getValue(Config::MOLLIE_SINGLE_CLICK_PAYMENT[$environment ? 'production' : 'sandbox']); $mollieImages = Tools::getValue(Config::MOLLIE_IMAGES); $showResentPayment = Tools::getValue(Config::MOLLIE_SHOW_RESEND_PAYMENT_LINK); - $mollieIssuers = Tools::getValue(Config::MOLLIE_ISSUERS); + $mollieIssuers = Tools::getValue(Config::MOLLIE_ISSUERS[$environment ? 'production' : 'sandbox']); $mollieCss = Tools::getValue(Config::MOLLIE_CSS); if (!isset($mollieCss)) { $mollieCss = ''; @@ -273,33 +277,33 @@ public function saveSettings(&$errors = []) if (empty($errors)) { if ($isBancontactQrCodeEnabled !== false) { - Configuration::updateValue(Config::MOLLIE_BANCONTACT_QR_CODE_ENABLED, $isBancontactQrCodeEnabled); + $this->configurationAdapter->updateValue(Config::MOLLIE_BANCONTACT_QR_CODE_ENABLED, $isBancontactQrCodeEnabled); } - Configuration::updateValue(Config::MOLLIE_APPLE_PAY_DIRECT, $isApplePayDirectEnabled); - Configuration::updateValue(Config::MOLLIE_APPLE_PAY_DIRECT_STYLE, $applePayDirectStyle); - Configuration::updateValue(Config::MOLLIE_API_KEY, $mollieApiKey); - Configuration::updateValue(Config::MOLLIE_API_KEY_TEST, $mollieApiKeyTest); - Configuration::updateValue(Config::MOLLIE_ENVIRONMENT, $environment); - Configuration::updateValue(Config::MOLLIE_PAYMENTSCREEN_LOCALE, $molliePaymentscreenLocale); - Configuration::updateValue(Config::MOLLIE_SEND_ORDER_CONFIRMATION, $mollieOrderConfirmationSand); - Configuration::updateValue(Config::MOLLIE_IFRAME, $mollieIFrameEnabled); - Configuration::updateValue(Config::MOLLIE_SINGLE_CLICK_PAYMENT, $mollieSingleClickPaymentEnabled); - Configuration::updateValue(Config::MOLLIE_IMAGES, $mollieImages); - Configuration::updateValue(Config::MOLLIE_SHOW_RESEND_PAYMENT_LINK, $showResentPayment); - Configuration::updateValue(Config::MOLLIE_ISSUERS, $mollieIssuers); - Configuration::updateValue(Config::MOLLIE_METHOD_COUNTRIES, (bool) $mollieMethodCountriesEnabled); - Configuration::updateValue(Config::MOLLIE_METHOD_COUNTRIES_DISPLAY, (bool) $mollieMethodCountriesDisplayEnabled); - Configuration::updateValue(Config::MOLLIE_CSS, $mollieCss); - Configuration::updateValue(Config::MOLLIE_DISPLAY_ERRORS, (int) $mollieErrors); - Configuration::updateValue(Config::MOLLIE_DEBUG_LOG, (int) $mollieLogger); - Configuration::updateValue(Config::MOLLIE_API, $mollieApi); - Configuration::updateValue(Config::MOLLIE_VOUCHER_CATEGORY, $voucherCategory); - Configuration::updateValue( + $this->configurationAdapter->updateValue(Config::MOLLIE_APPLE_PAY_DIRECT, $isApplePayDirectEnabled); + $this->configurationAdapter->updateValue(Config::MOLLIE_APPLE_PAY_DIRECT_STYLE, $applePayDirectStyle); + $this->configurationAdapter->updateValue(Config::MOLLIE_API_KEY, $mollieApiKey); + $this->configurationAdapter->updateValue(Config::MOLLIE_API_KEY_TEST, $mollieApiKeyTest); + $this->configurationAdapter->updateValue(Config::MOLLIE_ENVIRONMENT, $environment); + $this->configurationAdapter->updateValue(Config::MOLLIE_PAYMENTSCREEN_LOCALE, $molliePaymentscreenLocale); + $this->configurationAdapter->updateValue(Config::MOLLIE_SEND_ORDER_CONFIRMATION, $mollieOrderConfirmationSand); + $this->configurationAdapter->updateValue(Config::MOLLIE_IFRAME, $mollieIFrameEnabled); + $this->configurationAdapter->updateValue(Config::MOLLIE_SINGLE_CLICK_PAYMENT, $mollieSingleClickPaymentEnabled); + $this->configurationAdapter->updateValue(Config::MOLLIE_IMAGES, $mollieImages); + $this->configurationAdapter->updateValue(Config::MOLLIE_SHOW_RESEND_PAYMENT_LINK, $showResentPayment); + $this->configurationAdapter->updateValue(Config::MOLLIE_ISSUERS, $mollieIssuers); + $this->configurationAdapter->updateValue(Config::MOLLIE_METHOD_COUNTRIES, (bool) $mollieMethodCountriesEnabled); + $this->configurationAdapter->updateValue(Config::MOLLIE_METHOD_COUNTRIES_DISPLAY, (bool) $mollieMethodCountriesDisplayEnabled); + $this->configurationAdapter->updateValue(Config::MOLLIE_CSS, $mollieCss); + $this->configurationAdapter->updateValue(Config::MOLLIE_DISPLAY_ERRORS, (int) $mollieErrors); + $this->configurationAdapter->updateValue(Config::MOLLIE_DEBUG_LOG, (int) $mollieLogger); + $this->configurationAdapter->updateValue(Config::MOLLIE_API, $mollieApi); + $this->configurationAdapter->updateValue(Config::MOLLIE_VOUCHER_CATEGORY, $voucherCategory); + $this->configurationAdapter->updateValue( Config::MOLLIE_AUTO_SHIP_STATUSES, json_encode($this->getStatusesValue(Config::MOLLIE_AUTO_SHIP_STATUSES)) ); - Configuration::updateValue(Config::MOLLIE_AUTO_SHIP_MAIN, (bool) $mollieShipMain); - Configuration::updateValue( + $this->configurationAdapter->updateValue(Config::MOLLIE_AUTO_SHIP_MAIN, (bool) $mollieShipMain); + $this->configurationAdapter->updateValue( Config::MOLLIE_TRACKING_URLS, json_encode(@json_decode(Tools::getValue(Config::MOLLIE_TRACKING_URLS))) ); diff --git a/src/Service/Shipment/ShipmentInformationSender.php b/src/Service/Shipment/ShipmentInformationSender.php index a1026d18b..85111e1e1 100644 --- a/src/Service/Shipment/ShipmentInformationSender.php +++ b/src/Service/Shipment/ShipmentInformationSender.php @@ -12,6 +12,7 @@ namespace Mollie\Service\Shipment; +use Mollie\Api\MollieApiClient; use Mollie\Api\Resources\Order as ApiOrder; use Mollie\Repository\PaymentMethodRepositoryInterface; use Mollie\Service\ShipmentServiceInterface; @@ -40,12 +41,14 @@ public function __construct( /** * {@inheritDoc} */ - public function sendShipmentInformation($apiGateway, Order $order) + public function sendShipmentInformation(?MollieApiClient $apiGateway, Order $order): void { if (empty($apiGateway)) { return; } + $payment = $this->paymentMethodRepository->getPaymentBy('order_id', (int) $order->id); + $apiOrder = $apiGateway->orders->get($payment['transaction_id']); if (empty($apiOrder)) { @@ -64,7 +67,7 @@ public function sendShipmentInformation($apiGateway, Order $order) * * @return bool */ - private function hasShippableItems(ApiOrder $apiOrder) + private function hasShippableItems(ApiOrder $apiOrder): bool { $shippableItems = 0; diff --git a/src/Service/Shipment/ShipmentInformationSenderInterface.php b/src/Service/Shipment/ShipmentInformationSenderInterface.php index 134ed7e0b..a8f0432eb 100644 --- a/src/Service/Shipment/ShipmentInformationSenderInterface.php +++ b/src/Service/Shipment/ShipmentInformationSenderInterface.php @@ -12,14 +12,15 @@ namespace Mollie\Service\Shipment; +use Mollie\Api\Exceptions\ApiException; use Mollie\Api\MollieApiClient; use Order; interface ShipmentInformationSenderInterface { /** - * @param MollieApiClient|null $apiGateway - * @param Order $order + * @throws ApiException + * @throws \Exception */ - public function sendShipmentInformation($apiGateway, Order $order); + public function sendShipmentInformation(?MollieApiClient $apiGateway, Order $order): void; } diff --git a/src/Service/TransactionService.php b/src/Service/TransactionService.php index 9bbc5aaaf..7f3b56e66 100644 --- a/src/Service/TransactionService.php +++ b/src/Service/TransactionService.php @@ -25,10 +25,12 @@ use Mollie\Api\Types\RefundStatus; use Mollie\Config\Config; use Mollie\Errors\Http\HttpStatusCode; +use Mollie\Exception\ShipmentCannotBeSentException; use Mollie\Exception\TransactionException; use Mollie\Handler\Order\OrderCreationHandler; -use Mollie\Handler\Order\OrderFeeHandler; +use Mollie\Handler\Order\OrderPaymentFeeHandler; use Mollie\Handler\Shipment\ShipmentSenderHandlerInterface; +use Mollie\Logger\PrestaLoggerInterface; use Mollie\Repository\PaymentMethodRepositoryInterface; use Mollie\Utility\MollieStatusUtility; use Mollie\Utility\NumberUtility; @@ -69,10 +71,14 @@ class TransactionService private $paymentMethodService; /** @var MollieOrderCreationService */ private $mollieOrderCreationService; - /** @var OrderFeeHandler */ - private $orderFeeHandler; + /** @var OrderPaymentFeeHandler */ + private $orderPaymentFeeHandler; /** @var ShipmentSenderHandlerInterface */ private $shipmentSenderHandler; + /** @var PrestaLoggerInterface */ + private $logger; + /** @var ExceptionService */ + private $exceptionService; public function __construct( Mollie $module, @@ -81,8 +87,10 @@ public function __construct( OrderCreationHandler $orderCreationHandler, PaymentMethodService $paymentMethodService, MollieOrderCreationService $mollieOrderCreationService, - OrderFeeHandler $orderFeeHandler, - ShipmentSenderHandlerInterface $shipmentSenderHandler + OrderPaymentFeeHandler $orderPaymentFeeHandler, + ShipmentSenderHandlerInterface $shipmentSenderHandler, + PrestaLoggerInterface $logger, + ExceptionService $exceptionService ) { $this->module = $module; $this->orderStatusService = $orderStatusService; @@ -90,8 +98,10 @@ public function __construct( $this->orderCreationHandler = $orderCreationHandler; $this->paymentMethodService = $paymentMethodService; $this->mollieOrderCreationService = $mollieOrderCreationService; - $this->orderFeeHandler = $orderFeeHandler; + $this->orderPaymentFeeHandler = $orderPaymentFeeHandler; $this->shipmentSenderHandler = $shipmentSenderHandler; + $this->logger = $logger; + $this->exceptionService = $exceptionService; } /** @@ -117,6 +127,7 @@ public function processTransaction($apiPayment) throw new TransactionException('Transaction failed', HttpStatusCode::HTTP_BAD_REQUEST); } + $orderDescription = $apiPayment->description ?? $apiPayment->orderNumber; $paymentMethod = $this->paymentMethodRepository->getPaymentBy('transaction_id', $apiPayment->id); @@ -210,13 +221,28 @@ public function processTransaction($apiPayment) if (!$orderId && $isPaymentFinished) { $orderId = $this->orderCreationHandler->createOrder($apiPayment, $cart->id, $isKlarnaOrder); + if (!$orderId) { throw new TransactionException('Order is already created', HttpStatusCode::HTTP_METHOD_NOT_ALLOWED); } + $apiPayment = $this->updateOrderDescription($apiPayment, $orderId); + $this->savePaymentStatus($apiPayment->id, $apiPayment->status, $orderId); + $order = new Order($orderId); - $this->shipmentSenderHandler->handleShipmentSender($this->module->getApiClient(), $order, new \OrderState($order->current_state)); + + try { + $this->shipmentSenderHandler->handleShipmentSender($this->module->getApiClient(), $order, new \OrderState($order->current_state)); + } catch (ShipmentCannotBeSentException $exception) { + $this->logger->error($this->exceptionService->getErrorMessageForException( + $exception, + [], + ['orderReference' => $order->reference] + )); + } catch (ApiException $exception) { + $this->logger->error($exception->getMessage()); + } } elseif ($apiPayment->amountRefunded) { if (strpos($apiPayment->orderNumber, OrderNumberUtility::ORDER_NUMBER_PREFIX) === 0) { if (!MollieStatusUtility::isPaymentFinished($apiPayment->status)) { @@ -470,7 +496,7 @@ private function handlePaymentDescription(Payment $apiPayment) return; } $apiPayment = $this->updatePaymentDescription($apiPayment, $orderId); - $this->orderFeeHandler->addOrderFee($orderId, $apiPayment); + $this->orderPaymentFeeHandler->addOrderPaymentFee($orderId, $apiPayment); $this->processTransaction($apiPayment); } else { throw new TransactionException('Transaction is no longer used', HttpStatusCode::HTTP_METHOD_NOT_ALLOWED); @@ -486,7 +512,7 @@ private function handleOrderDescription(MollieOrderAlias $apiPayment) return; } $apiPayment = $this->updateOrderDescription($apiPayment, $orderId); - $this->orderFeeHandler->addOrderFee($orderId, $apiPayment); + $this->orderPaymentFeeHandler->addOrderPaymentFee($orderId, $apiPayment); $this->processTransaction($apiPayment); } else { throw new TransactionException('Transaction is no longer used', HttpStatusCode::HTTP_METHOD_NOT_ALLOWED); diff --git a/src/ServiceProvider/BaseServiceProvider.php b/src/ServiceProvider/BaseServiceProvider.php index 25de827b9..555387557 100644 --- a/src/ServiceProvider/BaseServiceProvider.php +++ b/src/ServiceProvider/BaseServiceProvider.php @@ -21,12 +21,14 @@ use Mollie\Handler\Shipment\ShipmentSenderHandler; use Mollie\Handler\Shipment\ShipmentSenderHandlerInterface; use Mollie\Install\UninstallerInterface; +use Mollie\Logger\PrestaLogger; +use Mollie\Logger\PrestaLoggerInterface; use Mollie\Provider\CreditCardLogoProvider; use Mollie\Provider\CustomLogoProviderInterface; use Mollie\Provider\EnvironmentVersionProvider; use Mollie\Provider\EnvironmentVersionProviderInterface; -use Mollie\Provider\OrderTotalProvider; -use Mollie\Provider\OrderTotalProviderInterface; +use Mollie\Provider\OrderTotal\OrderTotalProvider; +use Mollie\Provider\OrderTotal\OrderTotalProviderInterface; use Mollie\Provider\PaymentFeeProvider; use Mollie\Provider\PaymentFeeProviderInterface; use Mollie\Provider\PaymentType\PaymentTypeIdentificationProviderInterface; @@ -39,19 +41,32 @@ use Mollie\Provider\Shipment\AutomaticShipmentSenderStatusesProviderInterface; use Mollie\Provider\UpdateMessageProvider; use Mollie\Provider\UpdateMessageProviderInterface; +use Mollie\Repository\AddressRepository; +use Mollie\Repository\AddressRepositoryInterface; use Mollie\Repository\CartRuleRepository; use Mollie\Repository\CartRuleRepositoryInterface; +use Mollie\Repository\CurrencyRepository; +use Mollie\Repository\CurrencyRepositoryInterface; +use Mollie\Repository\GenderRepository; +use Mollie\Repository\GenderRepositoryInterface; use Mollie\Repository\MolCustomerRepository; +use Mollie\Repository\MolOrderPaymentFeeRepository; +use Mollie\Repository\MolOrderPaymentFeeRepositoryInterface; use Mollie\Repository\OrderRepository; use Mollie\Repository\OrderRepositoryInterface; use Mollie\Repository\PaymentMethodRepository; use Mollie\Repository\PaymentMethodRepositoryInterface; use Mollie\Repository\PendingOrderCartRuleRepository; use Mollie\Repository\PendingOrderCartRuleRepositoryInterface; +use Mollie\Repository\TaxRepository; +use Mollie\Repository\TaxRepositoryInterface; +use Mollie\Repository\TaxRuleRepository; +use Mollie\Repository\TaxRuleRepositoryInterface; +use Mollie\Repository\TaxRulesGroupRepository; +use Mollie\Repository\TaxRulesGroupRepositoryInterface; use Mollie\Service\ApiKeyService; use Mollie\Service\Content\SmartyTemplateParser; use Mollie\Service\Content\TemplateParserInterface; -use Mollie\Service\ExceptionService; use Mollie\Service\PaymentMethod\PaymentMethodRestrictionValidation; use Mollie\Service\PaymentMethod\PaymentMethodRestrictionValidation\ApplePayPaymentMethodRestrictionValidator; use Mollie\Service\PaymentMethod\PaymentMethodRestrictionValidation\BasePaymentMethodRestrictionValidator; @@ -99,10 +114,13 @@ public function register(Container $container) { /* Logger */ $this->addService($container, LoggerInterface::class, $container->get(Logger::class)); + $this->addService($container, PrestaLoggerInterface::class, $container->get(PrestaLogger::class)); + /* Utility */ $this->addService($container, ClockInterface::class, $container->get(Clock::class)); $this->addService($container, PaymentMethodRepositoryInterface::class, $container->get(PaymentMethodRepository::class)); + $this->addService($container, GenderRepositoryInterface::class, $container->get(GenderRepository::class)); $this->addService($container, MolCustomerRepository::class, MolCustomerRepository::class) ->withArgument('MolCustomer'); @@ -130,13 +148,17 @@ public function register(Container $container) [ $container->get(ShipmentVerificationInterface::class), $container->get(ShipmentInformationSenderInterface::class), - $container->get(ExceptionService::class), - $container->get(Logger::class), ] ); + $this->addService($container, AddressRepositoryInterface::class, $container->get(AddressRepository::class)); + $this->addService($container, TaxRulesGroupRepositoryInterface::class, $container->get(TaxRulesGroupRepository::class)); + $this->addService($container, TaxRuleRepositoryInterface::class, $container->get(TaxRuleRepository::class)); + $this->addService($container, TaxRepositoryInterface::class, $container->get(TaxRepository::class)); + $this->addService($container, OrderTotalProviderInterface::class, $container->get(OrderTotalProvider::class)); $this->addService($container, PaymentFeeProviderInterface::class, $container->get(PaymentFeeProvider::class)); + $this->addService($container, EnvironmentVersionProviderInterface::class, $container->get(EnvironmentVersionProvider::class)); $this->addService($container, AccessibilityCheckerInterface::class, $container->get(SubscriptionCancelAccessibility::class)); @@ -144,6 +166,8 @@ public function register(Container $container) $this->addService($container, PendingOrderCartRuleRepositoryInterface::class, $container->get(PendingOrderCartRuleRepository::class)); $this->addService($container, CartRuleRepositoryInterface::class, $container->get(CartRuleRepository::class)); $this->addService($container, OrderRepositoryInterface::class, $container->get(OrderRepository::class)); + $this->addService($container, CurrencyRepositoryInterface::class, $container->get(CurrencyRepository::class)); + $this->addService($container, MolOrderPaymentFeeRepositoryInterface::class, $container->get(MolOrderPaymentFeeRepository::class)); $this->addService($container, CartRuleQuantityChangeHandlerInterface::class, $container->get(CartRuleQuantityChangeHandler::class)); $this->addService($container, RecurringOrderRepositoryInterface::class, RecurringOrderRepository::class) diff --git a/src/Utility/NumberUtility.php b/src/Utility/NumberUtility.php index 02c13a7cf..f34ea296a 100644 --- a/src/Utility/NumberUtility.php +++ b/src/Utility/NumberUtility.php @@ -12,10 +12,28 @@ namespace Mollie\Utility; +use PrestaShop\Decimal\DecimalNumber; use PrestaShop\Decimal\Number; +use PrestaShop\Decimal\Operation\Rounding; class NumberUtility { + public const DECIMAL_PRECISION = 2; + public const FLOAT_PRECISION = 6; + private const ROUNDING = Rounding::ROUND_HALF_UP; + + // TODO make all methods consistent: either pass string/float as parameter or cast members to Number/DecimalNumber class beforehand. + + public static function toPrecision( + float $number, + int $precision = self::DECIMAL_PRECISION, + string $roundingMode = self::ROUNDING + ): float { + $decimalNumber = self::getNumber($number); + + return (float) $decimalNumber->toPrecision($precision, $roundingMode); + } + /** * Decreases number by its given percentage * E.g 75/1.5 = 50. @@ -32,9 +50,9 @@ public static function decreaseByPercentage($number, $percentage) if (!$percentage || $percentage <= 0) { return $number; } - $numberTransformed = self::toObject($number); + $numberTransformed = self::getNumber($number); $totalDecrease = self::toPercentageIncrease($percentage); - $decrement = (string) $numberTransformed->dividedBy(self::toObject($totalDecrease)); + $decrement = (string) $numberTransformed->dividedBy(self::getNumber($totalDecrease)); return (float) $decrement; } @@ -44,9 +62,9 @@ public static function increaseByPercentage($number, $percentage) if (!$percentage || $percentage <= 0) { return $number; } - $numberTransformed = self::toObject($number); + $numberTransformed = self::getNumber($number); $percentageIncrease = self::toPercentageIncrease($percentage); - $percentageIncreaseTransformed = self::toObject($percentageIncrease); + $percentageIncreaseTransformed = self::getNumber($percentageIncrease); $result = (string) $numberTransformed->times($percentageIncreaseTransformed); return (float) $result; @@ -61,86 +79,85 @@ public static function increaseByPercentage($number, $percentage) */ public static function toPercentageIncrease($percentage) { - $percentageNumber = self::toObject($percentage); - $smallerNumber = $percentageNumber->dividedBy(self::toObject(100)); - $result = (string) $smallerNumber->plus(self::toObject(1)); + $percentageNumber = self::getNumber($percentage); + $smallerNumber = $percentageNumber->dividedBy(self::getNumber(100)); + $result = (string) $smallerNumber->plus(self::getNumber(1)); return (float) $result; } - /** - * ($a*$b). - * - * @param float $a - * @param float $b - * - * @return float - */ - public static function times($a, $b) - { - $firstNumber = self::toObject($a); - $secondNumber = self::toObject($b); - $result = (string) $firstNumber->times($secondNumber); + public static function times( + float $target, + float $factor, + int $precision = self::FLOAT_PRECISION, + string $roundingMode = self::ROUNDING + ): float { + $firstNumber = self::getNumber($target); + $secondNumber = self::getNumber($factor); - return (float) $result; + $result = $firstNumber->times($secondNumber); + + return (float) $result->toPrecision($precision, $roundingMode); } - /** - * ($a/$b). - * - * @param float $a - * @param float $b - * @param int $precision - * - * @return float - * - * @throws \PrestaShop\Decimal\Exception\DivisionByZeroException - */ - public static function divide($a, $b, $precision = 20) - { - $firstNumber = self::toObject($a); - $secondNumber = self::toObject($b); - $result = (string) $firstNumber->dividedBy($secondNumber, $precision); + public static function divide( + float $target, + float $divisor, + int $precision = self::FLOAT_PRECISION, + string $roundingMode = self::ROUNDING + ): float { + $firstNumber = self::getNumber($target); + $secondNumber = self::getNumber($divisor); - return (float) $result; + $result = $firstNumber->dividedBy($secondNumber, $precision); + + return (float) $result->toPrecision($precision, $roundingMode); } public static function isEqual($a, $b) { - $firstNumber = self::toObject($a); - $secondNumber = self::toObject($b); + $firstNumber = self::getNumber($a); + $secondNumber = self::getNumber($b); return $firstNumber->equals($secondNumber); } public static function isLowerThan($a, $b) { - $firstNumber = self::toObject($a); - $secondNumber = self::toObject($b); + $firstNumber = self::getNumber($a); + $secondNumber = self::getNumber($b); return $firstNumber->isLowerThan($secondNumber); } public static function isLowerOrEqualThan($a, $b) { - $firstNumber = self::toObject($a); - $secondNumber = self::toObject($b); + $firstNumber = self::getNumber($a); + $secondNumber = self::getNumber($b); return $firstNumber->isLowerOrEqualThan($secondNumber); } + public static function isGreaterThan(float $target, float $comparison): bool + { + $firstNumber = self::getNumber($target); + $secondNumber = self::getNumber($comparison); + + return $firstNumber->isGreaterThan($secondNumber); + } + public static function minus($a, $b) { - $firstNumber = self::toObject($a); - $secondNumber = self::toObject($b); + $firstNumber = self::getNumber($a); + $secondNumber = self::getNumber($b); return (float) ((string) $firstNumber->minus($secondNumber)); } public static function plus($a, $b) { - $firstNumber = self::toObject($a); - $secondNumber = self::toObject($b); + $firstNumber = self::getNumber($a); + $secondNumber = self::getNumber($b); return (float) ((string) $firstNumber->plus($secondNumber)); } @@ -148,10 +165,14 @@ public static function plus($a, $b) /** * @param float $number * - * @return Number + * @return Number|DecimalNumber */ - private static function toObject($number) + private static function getNumber(float $number) { + if (is_subclass_of(Number::class, DecimalNumber::class)) { + return new DecimalNumber((string) $number); + } + return new Number((string) $number); } } diff --git a/src/Utility/PaymentFeeUtility.php b/src/Utility/PaymentFeeUtility.php deleted file mode 100644 index db68960c5..000000000 --- a/src/Utility/PaymentFeeUtility.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @copyright Mollie B.V. - * @license https://github.com/mollie/PrestaShop/blob/master/LICENSE.md - * - * @see https://github.com/mollie/PrestaShop - * @codingStandardsIgnoreStart - */ - -namespace Mollie\Utility; - -use Mollie\Config\Config; -use MolPaymentMethod; -use PrestaShop\Decimal\Number; - -class PaymentFeeUtility -{ - public static function getPaymentFee(MolPaymentMethod $paymentMethod, $totalCartPrice) - { - switch ($paymentMethod->surcharge) { - case Config::FEE_FIXED_FEE: - $totalFeePrice = new Number($paymentMethod->surcharge_fixed_amount); - break; - case Config::FEE_PERCENTAGE: - $totalCartPrice = new Number((string) $totalCartPrice); - $surchargePercentage = new Number($paymentMethod->surcharge_percentage); - $maxPercentage = new Number('100'); - $totalFeePrice = $totalCartPrice->times( - $surchargePercentage->dividedBy( - $maxPercentage - ) - ); - break; - case Config::FEE_FIXED_FEE_AND_PERCENTAGE: - $totalCartPrice = new Number((string) $totalCartPrice); - $surchargePercentage = new Number($paymentMethod->surcharge_percentage); - $maxPercentage = new Number('100'); - $surchargeFixedPrice = new Number($paymentMethod->surcharge_fixed_amount); - $totalFeePrice = $totalCartPrice->times( - $surchargePercentage->dividedBy( - $maxPercentage - ) - )->plus($surchargeFixedPrice); - break; - case Config::FEE_NO_FEE: - default: - return false; - } - - $surchargeMaxValue = new Number($paymentMethod->surcharge_limit); - $zero = new Number('0'); - if ($surchargeMaxValue->isGreaterThan($zero) && $totalFeePrice->isGreaterOrEqualThan($surchargeMaxValue)) { - $totalFeePrice = $surchargeMaxValue; - } - - return $totalFeePrice->toPrecision(2); - } -} diff --git a/src/Verification/IsPaymentInformationAvailable.php b/src/Verification/IsPaymentInformationAvailable.php new file mode 100644 index 000000000..652bc73e1 --- /dev/null +++ b/src/Verification/IsPaymentInformationAvailable.php @@ -0,0 +1,28 @@ +paymentMethodRepository = $paymentMethodRepository; + } + + public function verify(int $orderId): bool + { + return $this->hasPaymentInformation($orderId); + } + + private function hasPaymentInformation(int $orderId): bool + { + $payment = $this->paymentMethodRepository->getPaymentBy('order_id', (int) $orderId); + + return !(empty($payment) || empty($payment['transaction_id'])); + } +} diff --git a/src/Verification/Shipment/CanSendShipment.php b/src/Verification/Shipment/CanSendShipment.php index 17b48818c..6393a25e3 100644 --- a/src/Verification/Shipment/CanSendShipment.php +++ b/src/Verification/Shipment/CanSendShipment.php @@ -21,6 +21,7 @@ use Mollie\Provider\Shipment\AutomaticShipmentSenderStatusesProviderInterface; use Mollie\Repository\PaymentMethodRepositoryInterface; use Mollie\Service\ShipmentServiceInterface; +use Mollie\Verification\IsPaymentInformationAvailable; use Order; use OrderState; use PrestaShopLogger; @@ -51,41 +52,45 @@ class CanSendShipment implements ShipmentVerificationInterface * @var ShipmentServiceInterface */ private $shipmentService; + /** @var IsPaymentInformationAvailable */ + private $isPaymentInformationAvailable; public function __construct( ConfigurationAdapter $configurationAdapter, AutomaticShipmentSenderStatusesProviderInterface $automaticShipmentSenderStatusesProvider, OrderEndpointPaymentTypeHandlerInterface $endpointPaymentTypeHandler, PaymentMethodRepositoryInterface $paymentMethodRepository, - ShipmentServiceInterface $shipmentService + ShipmentServiceInterface $shipmentService, + IsPaymentInformationAvailable $isPaymentInformationAvailable ) { $this->automaticShipmentSenderStatusesProvider = $automaticShipmentSenderStatusesProvider; $this->configurationAdapter = $configurationAdapter; $this->endpointPaymentTypeHandler = $endpointPaymentTypeHandler; $this->paymentMethodRepository = $paymentMethodRepository; $this->shipmentService = $shipmentService; + $this->isPaymentInformationAvailable = $isPaymentInformationAvailable; } /** * {@inheritDoc} */ - public function verify(Order $order, OrderState $orderState) + public function verify(Order $order, OrderState $orderState): bool { /* todo: doesnt work with no tracking information. Will need to create new validation */ // if (!$this->hasShipmentInformation($order->reference)) { // throw new ShipmentCannotBeSentException('Shipment information cannot be sent. No shipment information found by order reference', ShipmentCannotBeSentException::NO_SHIPPING_INFORMATION, $order->reference); // } - if (!$this->isAutomaticShipmentAvailable($orderState->id)) { - throw new ShipmentCannotBeSentException('Shipment information cannot be sent. Automatic shipment sender is not available', ShipmentCannotBeSentException::AUTOMATIC_SHIPMENT_SENDER_IS_NOT_AVAILABLE, $order->reference); + if (!$this->isAutomaticShipmentAvailable((int) $orderState->id)) { + return false; } - if (!$this->hasPaymentInformation($order->id)) { - return false; + if (!$this->isPaymentInformationAvailable->verify((int) $order->id)) { + throw new ShipmentCannotBeSentException('Shipment information cannot be sent. Missing payment information', ShipmentCannotBeSentException::ORDER_HAS_NO_PAYMENT_INFORMATION, $order->reference); } - if (!$this->isRegularPayment($order->id)) { - return false; + if (!$this->isRegularPayment((int) $order->id)) { + throw new ShipmentCannotBeSentException('Shipment information cannot be sent. Is regular payment', ShipmentCannotBeSentException::PAYMENT_IS_NOT_ORDER, $order->reference); } return true; @@ -96,20 +101,17 @@ public function verify(Order $order, OrderState $orderState) * * @return bool */ - private function isRegularPayment($orderId) + private function isRegularPayment(int $orderId): bool { $payment = $this->paymentMethodRepository->getPaymentBy('order_id', (int) $orderId); if (empty($payment)) { return false; } - $paymentType = $this->endpointPaymentTypeHandler->getPaymentTypeFromTransactionId($payment['transaction_id']); - if ((int) $paymentType !== PaymentTypeEnum::PAYMENT_TYPE_ORDER) { - return false; - } + $paymentType = $this->endpointPaymentTypeHandler->getPaymentTypeFromTransactionId($payment['transaction_id']); - return true; + return (int) $paymentType === PaymentTypeEnum::PAYMENT_TYPE_ORDER; } /** @@ -117,7 +119,7 @@ private function isRegularPayment($orderId) * * @return bool */ - private function isAutomaticShipmentAvailable($orderStateId) + private function isAutomaticShipmentAvailable(int $orderStateId): bool { if (!$this->isAutomaticShipmentInformationSenderEnabled()) { return false; @@ -130,32 +132,12 @@ private function isAutomaticShipmentAvailable($orderStateId) return true; } - /** - * @param int $orderId - * - * @return bool - */ - private function hasPaymentInformation($orderId) - { - $payment = $this->paymentMethodRepository->getPaymentBy('order_id', (int) $orderId); - - if (empty($payment)) { - return false; - } - - if (empty($payment['transaction_id'])) { - return false; - } - - return true; - } - /** * @param string $orderReference * * @return bool */ - private function hasShipmentInformation($orderReference) + private function hasShipmentInformation(string $orderReference): bool { try { return !empty($this->shipmentService->getShipmentInformation($orderReference)); @@ -169,7 +151,7 @@ private function hasShipmentInformation($orderReference) /** * @return bool */ - private function isAutomaticShipmentInformationSenderEnabled() + private function isAutomaticShipmentInformationSenderEnabled(): bool { return (bool) $this->configurationAdapter->get(Config::MOLLIE_AUTO_SHIP_MAIN); } @@ -179,11 +161,15 @@ private function isAutomaticShipmentInformationSenderEnabled() * * @return bool */ - private function isOrderStateInAutomaticShipmentSenderOrderStateList($orderStateId) + private function isOrderStateInAutomaticShipmentSenderOrderStateList(int $orderStateId): bool { return in_array( - (int) $orderStateId, - array_map('intval', $this->automaticShipmentSenderStatusesProvider->getAutomaticShipmentSenderStatuses()) + $orderStateId, + array_map( + 'intval', + $this->automaticShipmentSenderStatusesProvider->getAutomaticShipmentSenderStatuses() + ), + true ); } } diff --git a/src/Verification/Shipment/ShipmentVerificationInterface.php b/src/Verification/Shipment/ShipmentVerificationInterface.php index 2f7b1dd8a..d2b5165eb 100644 --- a/src/Verification/Shipment/ShipmentVerificationInterface.php +++ b/src/Verification/Shipment/ShipmentVerificationInterface.php @@ -22,6 +22,8 @@ interface ShipmentVerificationInterface * @param Order $order * @param OrderState $orderState * + * @returns bool + * * @throws ShipmentCannotBeSentException */ public function verify(Order $order, OrderState $orderState); diff --git a/tests/Integration/Install/OrderStateInstallerTest.php b/tests/Integration/Install/OrderStateInstallerTest.php new file mode 100644 index 000000000..a6f5598ae --- /dev/null +++ b/tests/Integration/Install/OrderStateInstallerTest.php @@ -0,0 +1,123 @@ +assertDatabaseHas(\Configuration::class, [ + 'name' => $key, + ]); + + //NOTE cannot just check by "name" Field name is declared as lang field but is used in non multilang context + $this->assertDatabaseHas(\OrderState::class, [ + 'id_order_state' => (int) \Configuration::get($key), + 'color' => $color, + 'send_email' => $sendEmail, + 'logable' => $logable, + 'delivery' => $delivery, + 'invoice' => $invoice, + 'shipped' => $shipped, + 'paid' => $paid, + 'pdf_invoice' => $pdfInvoice, + 'module_name' => 'mollie', + ]); + } + + public function requiredOrderStatusesDataProvider() + { + return [ + [ + 'key' => Config::MOLLIE_STATUS_PARTIAL_REFUND, + 'color' => '#6F8C9F', + 'sendEmail' => false, + 'logable' => false, + 'delivery' => false, + 'invoice' => false, + 'shipped' => false, + 'paid' => false, + 'pdfInvoice' => false, + ], + [ + 'key' => Config::MOLLIE_STATUS_AWAITING, + 'color' => '#4169E1', + 'sendEmail' => false, + 'logable' => false, + 'delivery' => false, + 'invoice' => false, + 'shipped' => false, + 'paid' => false, + 'pdfInvoice' => false, + ], + [ + 'key' => Config::MOLLIE_STATUS_PARTIALLY_SHIPPED, + 'color' => '#8A2BE2', + 'sendEmail' => false, + 'logable' => false, + 'delivery' => false, + 'invoice' => false, + 'shipped' => false, + 'paid' => false, + 'pdfInvoice' => false, + ], + [ + 'key' => Config::MOLLIE_STATUS_ORDER_COMPLETED, + 'color' => '#3d7d1c', + 'sendEmail' => true, + 'logable' => false, + 'delivery' => false, + 'invoice' => false, + 'shipped' => false, + 'paid' => false, + 'pdfInvoice' => false, + ], + [ + 'key' => Config::MOLLIE_STATUS_KLARNA_AUTHORIZED, + 'color' => '#8A2BE2', + 'sendEmail' => true, + 'logable' => true, + 'delivery' => false, + 'invoice' => true, + 'shipped' => false, + 'paid' => true, + 'pdfInvoice' => true, + ], + [ + 'key' => Config::MOLLIE_STATUS_KLARNA_SHIPPED, + 'color' => '#8A2BE2', + 'sendEmail' => true, + 'logable' => true, + 'delivery' => true, + 'invoice' => false, + 'shipped' => true, + 'paid' => true, + 'pdfInvoice' => true, + ], + [ + 'key' => Config::MOLLIE_STATUS_CHARGEBACK, + 'color' => '#E74C3C', + 'sendEmail' => false, + 'logable' => false, + 'delivery' => false, + 'invoice' => false, + 'shipped' => false, + 'paid' => false, + 'pdfInvoice' => false, + ], + ]; + } +} diff --git a/tests/Integration/Subscription/Factory/TestCreateSubscriptionData.php b/tests/Integration/Subscription/Factory/TestCreateSubscriptionData.php index 978165c10..c70d746c7 100644 --- a/tests/Integration/Subscription/Factory/TestCreateSubscriptionData.php +++ b/tests/Integration/Subscription/Factory/TestCreateSubscriptionData.php @@ -40,6 +40,7 @@ public function testBuild() [ 'method' => MandateMethod::CREDITCARD, 'name' => self::CUSTOMER_NAME, + 'mandate_id' => 'test-mandate-id', ] ); @@ -53,6 +54,7 @@ public function testBuild() [ 'method' => MandateMethod::CREDITCARD, 'name' => self::CUSTOMER_NAME, + 'mandate_id' => 'test-mandate-id', ] ); $combinationMock = $this->createMock(\Combination::class); @@ -68,6 +70,10 @@ public function testBuild() $combinationRepositoryMock->method('getById')->willReturn($combinationMock); $customerRepository = new MolCustomerRepository('MolCustomer'); + + $link = $this->createMock(\Mollie\Adapter\Link::class); + $link->method('getModuleLink')->willReturn('test-link'); + /** @var CreateSubscriptionDataFactory $createSubscriptionData */ $createSubscriptionData = new CreateSubscriptionDataFactory( $customerRepository, @@ -75,7 +81,9 @@ public function testBuild() new SubscriptionDescriptionProvider(), new CurrencyRepository(), $combinationRepositoryMock, - $paymentMethodMock + $paymentMethodMock, + $link, + new Mollie() ); $customer = $this->createMock('Customer'); @@ -83,6 +91,7 @@ public function testBuild() $orderMock = $this->createMock('Order'); $orderMock->id = 9999; + $orderMock->reference = 'REFERENCE123'; $orderMock->id_currency = 1; $orderMock->total_paid_tax_incl = 19.99; $orderMock->method('getCustomer')->willReturn($customer); @@ -104,8 +113,12 @@ public function testBuild() 'currency' => 'EUR', ], 'interval' => '1 day', - 'description' => 'mol-9999-19.99-EUR', - 'method' => MandateMethod::CREDITCARD, + 'description' => 'subscription-REFERENCE123', + 'webhookUrl' => 'test-link', + 'mandateId' => 'test-mandate-id', + 'metadata' => [ + 'secure_key' => $subscriptionData->getMetaData()['secure_key'], //NOTE: cannot really mock static methods. + ], ], $subscriptionData->jsonSerialize() ); diff --git a/tests/Integration/Subscription/Validator/SubscriptionCartTest.php b/tests/Integration/Subscription/Validator/SubscriptionCartTest.php index 8a680dae6..248217403 100644 --- a/tests/Integration/Subscription/Validator/SubscriptionCartTest.php +++ b/tests/Integration/Subscription/Validator/SubscriptionCartTest.php @@ -78,7 +78,8 @@ public function testValidate(string $combinationReference, bool $hasExtraAttribu new ProductCombinationRepository(), new CombinationRepository(), new ProductAttributeAdapter() - ) + ), + $this->getService(\Mollie\Adapter\ToolsAdapter::class) ); if ($expectedResult !== true) { diff --git a/tests/Mollie/Tests/Unit/Utility/NumberUtilityTest.php b/tests/Mollie/Tests/Unit/Utility/NumberUtilityTest.php new file mode 100644 index 000000000..3d3e81b48 --- /dev/null +++ b/tests/Mollie/Tests/Unit/Utility/NumberUtilityTest.php @@ -0,0 +1,53 @@ +assertEquals(6.57, $result); + + $result = NumberUtility::toPrecision(6.56139, 2); + + $this->assertEquals(6.56, $result); + } + + public function testItSuccessfullyMultipliesNumber(): void + { + $result = NumberUtility::times(6.56, 2.57); + + $this->assertEquals(16.8592, $result); + + $result = NumberUtility::times(6.56, 2.57, 2); + + $this->assertEquals(16.86, $result); + + $result = NumberUtility::times(0, 0); + + $this->assertEquals(0, $result); + } + + public function testItSuccessfullyDividesNumber(): void + { + $result = NumberUtility::divide(6.56, 2.5); + + $this->assertEquals(2.624, $result); + + $result = NumberUtility::divide(6.56, 2.5, 2); + + $this->assertEquals(2.62, $result); + } + + public function testItSuccessfullyChecksIsGreaterThan(): void + { + $result = NumberUtility::isGreaterThan(6.56, 6.556); + + $this->assertEquals(true, $result); + } +} diff --git a/tests/Unit/Builder/InvoicePdfTemplateBuilderTest.php b/tests/Unit/Builder/InvoicePdfTemplateBuilderTest.php new file mode 100644 index 000000000..17bc53974 --- /dev/null +++ b/tests/Unit/Builder/InvoicePdfTemplateBuilderTest.php @@ -0,0 +1,124 @@ +molOrderPaymentFeeRepository = $this->createMock(MolOrderPaymentFeeRepositoryInterface::class); + $this->currencyRepository = $this->createMock(CurrencyRepositoryInterface::class); + } + + public function testItSuccessfullyBuildsTemplate(): void + { + $molOrderPaymentFee = $this->createMock(MolOrderPaymentFee::class); + $molOrderPaymentFee->fee_tax_incl = 10.00; + + $this->molOrderPaymentFeeRepository->expects($this->once())->method('findOneBy')->willReturn($molOrderPaymentFee); + + $orderCurrency = $this->createMock(\Currency::class); + $orderCurrency->iso_code = 'USD'; + + $this->currencyRepository->expects($this->once())->method('findOneBy')->willReturn($orderCurrency); + + $invoicePdfTemplateBuilder = new InvoicePdfTemplateBuilder( + $this->molOrderPaymentFeeRepository, + $this->currencyRepository + ); + + $order = $this->createMock(\Order::class); + $order->id = 1; + $order->id_currency = 1; + + $locale = $this->createMock(Locale::class); + $locale->expects($this->once())->method('formatPrice')->willReturn('$ 10.00'); + + $result = $invoicePdfTemplateBuilder + ->setOrder($order) + ->setLocale($locale) + ->buildParams(); + + $expectedResult = [ + 'orderFeeAmountDisplay' => '$ 10.00', + ]; + + $this->assertEquals($expectedResult, $result); + } + + public function testItUnsuccessfullyBuildsTemplateFailedToFindOrderPaymentFee(): void + { + $this->molOrderPaymentFeeRepository->expects($this->once())->method('findOneBy')->willReturn(null); + + $orderCurrency = $this->createMock(\Currency::class); + $orderCurrency->iso_code = 'USD'; + + $this->currencyRepository->expects($this->never())->method('findOneBy')->willReturn($orderCurrency); + + $invoicePdfTemplateBuilder = new InvoicePdfTemplateBuilder( + $this->molOrderPaymentFeeRepository, + $this->currencyRepository + ); + + $order = $this->createMock(\Order::class); + $order->id = 1; + $order->id_currency = 1; + + $locale = $this->createMock(Locale::class); + $locale->expects($this->never())->method('formatPrice')->willReturn('$ 10.00'); + + $result = $invoicePdfTemplateBuilder + ->setOrder($order) + ->setLocale($locale) + ->buildParams(); + + $expectedResult = []; + + $this->assertEquals($expectedResult, $result); + } + + public function testItUnsuccessfullyBuildsTemplateFailedToFindOrderCurrency(): void + { + $molOrderPaymentFee = $this->createMock(MolOrderPaymentFee::class); + $molOrderPaymentFee->fee_tax_incl = 10.00; + + $this->molOrderPaymentFeeRepository->expects($this->once())->method('findOneBy')->willReturn($molOrderPaymentFee); + + $this->currencyRepository->expects($this->once())->method('findOneBy')->willReturn(null); + + $invoicePdfTemplateBuilder = new InvoicePdfTemplateBuilder( + $this->molOrderPaymentFeeRepository, + $this->currencyRepository + ); + + $order = $this->createMock(\Order::class); + $order->id = 1; + $order->id_currency = 1; + + $locale = $this->createMock(Locale::class); + $locale->expects($this->never())->method('formatPrice')->willReturn('$ 10.00'); + + $result = $invoicePdfTemplateBuilder + ->setOrder($order) + ->setLocale($locale) + ->buildParams(); + + $expectedResult = []; + + $this->assertEquals($expectedResult, $result); + } +} diff --git a/tests/Unit/Calculator/PaymentFeeCalculatorTest.php b/tests/Unit/Calculator/PaymentFeeCalculatorTest.php new file mode 100644 index 000000000..b7e33a7a3 --- /dev/null +++ b/tests/Unit/Calculator/PaymentFeeCalculatorTest.php @@ -0,0 +1,129 @@ +taxCalculator = $this->createMock(TaxCalculator::class); + $this->context = $this->createMock(Context::class); + } + + public function testItCalculatesFixedFee(): void + { + $this->taxCalculator->method('addTaxes')->willReturn(11.0); + $this->taxCalculator->method('getTotalRate')->willReturn(10.0); + + $this->context->method('getComputingPrecision')->willReturn(2); + + $paymentFeeCalculator = new PaymentFeeCalculator($this->taxCalculator, $this->context); + + $result = $paymentFeeCalculator->calculateFixedFee(10); + + $this->assertEquals(11.0, $result->getPaymentFeeTaxIncl()); + $this->assertEquals(10.0, $result->getPaymentFeeTaxExcl()); + $this->assertEquals(10.0, $result->getTaxRate()); + $this->assertEquals(true, $result->isActive()); + } + + public function testItCalculatesPercentageFeeWithoutReachedLimit(): void + { + $this->taxCalculator->method('removeTaxes')->willReturn(1.0); + $this->taxCalculator->method('getTotalRate')->willReturn(10.0); + + $this->context->method('getComputingPrecision')->willReturn(2); + + $paymentFeeCalculator = new PaymentFeeCalculator($this->taxCalculator, $this->context); + + $result = $paymentFeeCalculator->calculatePercentageFee( + 11, + 10, + 10 + ); + + $this->assertEquals(1.1, $result->getPaymentFeeTaxIncl()); + $this->assertEquals(1.0, $result->getPaymentFeeTaxExcl()); + $this->assertEquals(10.0, $result->getTaxRate()); + $this->assertEquals(true, $result->isActive()); + } + + public function testItCalculatesPercentageFeeWithReachedLimit(): void + { + $this->taxCalculator->method('removeTaxes')->willReturn(10.0); + $this->taxCalculator->method('getTotalRate')->willReturn(10.0); + + $this->context->method('getComputingPrecision')->willReturn(2); + + $paymentFeeCalculator = new PaymentFeeCalculator($this->taxCalculator, $this->context); + + $result = $paymentFeeCalculator->calculatePercentageFee( + 200, + 10, + 11 + ); + + $this->assertEquals(11.0, $result->getPaymentFeeTaxIncl()); + $this->assertEquals(10.0, $result->getPaymentFeeTaxExcl()); + $this->assertEquals(10.0, $result->getTaxRate()); + $this->assertEquals(true, $result->isActive()); + } + + public function testItCalculatesPercentageAndFixedPriceFeeWithoutReachedLimit(): void + { + $this->taxCalculator->method('addTaxes')->willReturn(11.0); + $this->taxCalculator->method('removeTaxes')->willReturn(18.9); + $this->taxCalculator->method('getTotalRate')->willReturn(10.0); + + $this->context->method('getComputingPrecision')->willReturn(2); + + $paymentFeeCalculator = new PaymentFeeCalculator($this->taxCalculator, $this->context); + + $result = $paymentFeeCalculator->calculatePercentageAndFixedPriceFee( + 100, + 10, + 10, + 100 + ); + + $this->assertEquals(21.0, $result->getPaymentFeeTaxIncl()); + $this->assertEquals(18.9, $result->getPaymentFeeTaxExcl()); + $this->assertEquals(10.0, $result->getTaxRate()); + $this->assertEquals(true, $result->isActive()); + } + + public function testItCalculatesPercentageAndFixedPriceFeeWithReachedLimit(): void + { + $this->taxCalculator->method('addTaxes')->willReturn(11.0); + $this->taxCalculator->method('removeTaxes')->willReturn(10.0); + $this->taxCalculator->method('getTotalRate')->willReturn(10.0); + + $this->context->method('getComputingPrecision')->willReturn(2); + + $paymentFeeCalculator = new PaymentFeeCalculator($this->taxCalculator, $this->context); + + $result = $paymentFeeCalculator->calculatePercentageAndFixedPriceFee( + 100, + 10, + 10, + 11 + ); + + $this->assertEquals(11.0, $result->getPaymentFeeTaxIncl()); + $this->assertEquals(10.0, $result->getPaymentFeeTaxExcl()); + $this->assertEquals(10.0, $result->getTaxRate()); + $this->assertEquals(true, $result->isActive()); + } +} diff --git a/tests/Unit/Collector/ApplePayDirect/OrderTotalCollectorTest.php b/tests/Unit/Collector/ApplePayDirect/OrderTotalCollectorTest.php index a568c819e..05e4b671d 100644 --- a/tests/Unit/Collector/ApplePayDirect/OrderTotalCollectorTest.php +++ b/tests/Unit/Collector/ApplePayDirect/OrderTotalCollectorTest.php @@ -1,8 +1,12 @@ createMock(OrderFeeService::class); - $orderFeeServiceMock->method('getPaymentFee')->willReturn(0.5); + $paymentFeeData = $this->createMock(PaymentFeeData::class); + $paymentFeeData->method('getPaymentFeeTaxIncl')->willReturn(0.5); + + $orderPaymentFeeService = $this->createMock(OrderPaymentFeeService::class); + $orderPaymentFeeService->method('getPaymentFee')->willReturn($paymentFeeData); $cart = $this->createMock(Cart::class); $cart->method('getOrderTotal')->willReturn(1.95); - $orderTotalCollector = new OrderTotalCollector($orderFeeServiceMock); + $orderTotalCollector = new OrderTotalCollector($orderPaymentFeeService); $orderTotals = $orderTotalCollector->getOrderTotals($carriers, $cart); $this->assertEquals($expectedResult, $orderTotals); } - public function getCarriersDataProvider() + public function getCarriersDataProvider(): array { return [ 'basic case' => [ diff --git a/tests/Unit/Handler/Shipment/ShipmentSenderHandlerTest.php b/tests/Unit/Handler/Shipment/ShipmentSenderHandlerTest.php index 51c6d6ad5..fce1377c5 100644 --- a/tests/Unit/Handler/Shipment/ShipmentSenderHandlerTest.php +++ b/tests/Unit/Handler/Shipment/ShipmentSenderHandlerTest.php @@ -3,8 +3,6 @@ use Mollie\Api\MollieApiClient; use Mollie\Exception\ShipmentCannotBeSentException; use Mollie\Handler\Shipment\ShipmentSenderHandler; -use Mollie\Logger\PrestaLogger; -use Mollie\Service\ExceptionService; use Mollie\Service\Shipment\ShipmentInformationSender; use Mollie\Verification\Shipment\CanSendShipment; use PHPUnit\Framework\MockObject\MockObject; @@ -37,16 +35,6 @@ class ShipmentSenderHandlerTest extends TestCase */ private $shipmentInformationSender; - /** - * @var ExceptionService|MockObject - */ - private $exceptionService; - - /** - * @var PrestaLogger|MockObject - */ - private $moduleLogger; - protected function setUp() { parent::setUp(); @@ -80,21 +68,9 @@ protected function setUp() ->disableOriginalConstructor() ->getMock() ; - - $this->exceptionService = $this - ->getMockBuilder(ExceptionService::class) - ->disableOriginalConstructor() - ->getMock() - ; - - $this->moduleLogger = $this - ->getMockBuilder(PrestaLogger::class) - ->disableOriginalConstructor() - ->getMock() - ; } - public function testCanSendShipment() + public function testCanSendShipment(): void { $this->canSendShipment ->expects($this->once()) @@ -102,34 +78,20 @@ public function testCanSendShipment() ->willReturn(true) ; - $this->exceptionService - ->expects($this->never()) - ->method('getErrorMessages') - ->willReturn([]) - ; - - $this->exceptionService - ->expects($this->never()) - ->method('getErrorMessageForException') - ; - - $this->moduleLogger - ->expects($this->never()) - ->method('error') + $this->shipmentInformationSender + ->expects($this->once()) + ->method('sendShipmentInformation') ; $shipmentSenderHandler = new ShipmentSenderHandler( $this->canSendShipment, - $this->shipmentInformationSender, - $this->exceptionService, - $this->moduleLogger + $this->shipmentInformationSender ); - $result = $shipmentSenderHandler->handleShipmentSender($this->apiClient, $this->order, $this->orderState); - $this->assertEquals(true, $result); + $shipmentSenderHandler->handleShipmentSender($this->apiClient, $this->order, $this->orderState); } - public function testOnVerificationExceptionLogExceptionAndNotSendInformation() + public function testItSuccessfullyFailsToSendShipmentExceptionThrown(): void { $this->order->reference = 'test'; @@ -137,36 +99,41 @@ public function testOnVerificationExceptionLogExceptionAndNotSendInformation() ->expects($this->once()) ->method('verify') ->willThrowException(new ShipmentCannotBeSentException( - 'Shipment information cannot be sent. No shipment information found by order reference', - ShipmentCannotBeSentException::NO_SHIPPING_INFORMATION, + '', + ShipmentCannotBeSentException::ORDER_HAS_NO_PAYMENT_INFORMATION, $this->order->reference )) ; - $this->exceptionService - ->expects($this->once()) - ->method('getErrorMessages') - ->willReturn([]) - ; + $shipmentSenderHandler = new ShipmentSenderHandler( + $this->canSendShipment, + $this->shipmentInformationSender + ); - $this->exceptionService + $this->expectException(ShipmentCannotBeSentException::class); + $this->expectExceptionCode(ShipmentCannotBeSentException::ORDER_HAS_NO_PAYMENT_INFORMATION); + + $shipmentSenderHandler->handleShipmentSender($this->apiClient, $this->order, $this->orderState); + } + + public function testItSuccessfullyFailsToSendShipmentVerificationReturnedFalse(): void + { + $this->canSendShipment ->expects($this->once()) - ->method('getErrorMessageForException') + ->method('verify') + ->willReturn(false) ; - $this->moduleLogger - ->expects($this->once()) - ->method('error') + $this->shipmentInformationSender + ->expects($this->never()) + ->method('sendShipmentInformation') ; $shipmentSenderHandler = new ShipmentSenderHandler( $this->canSendShipment, - $this->shipmentInformationSender, - $this->exceptionService, - $this->moduleLogger + $this->shipmentInformationSender ); - $result = $shipmentSenderHandler->handleShipmentSender($this->apiClient, $this->order, $this->orderState); - $this->assertEquals(false, $result); + $shipmentSenderHandler->handleShipmentSender($this->apiClient, $this->order, $this->orderState); } } diff --git a/tests/Unit/Provider/PaymentFeeProviderTest.php b/tests/Unit/Provider/PaymentFeeProviderTest.php new file mode 100644 index 000000000..b6eee7744 --- /dev/null +++ b/tests/Unit/Provider/PaymentFeeProviderTest.php @@ -0,0 +1,214 @@ +context = $this->createMock(Context::class); + $this->addressRepository = $this->createMock(AddressRepositoryInterface::class); + $this->taxCalculatorProvider = $this->createMock(TaxCalculatorProvider::class); + + $this->molPaymentMethod = $this->createMock(MolPaymentMethod::class); + $this->address = $this->createMock(Address::class); + $this->taxCalculator = $this->createMock(TaxCalculator::class); + } + + public function testItSuccessfullyProvidesFixedPaymentFee(): void + { + $this->molPaymentMethod->surcharge = Config::FEE_FIXED_FEE; + $this->molPaymentMethod->surcharge_percentage = 0; + $this->molPaymentMethod->surcharge_limit = 0; + $this->molPaymentMethod->surcharge_fixed_amount_tax_excl = 10; + $this->molPaymentMethod->tax_rules_group_id = 1; + + $this->address = $this->createMock(Address::class); + + $this->address->id = 1; + $this->address->id_country = 1; + $this->address->id_state = 0; + + $this->addressRepository->method('findOneBy')->willReturn($this->address); + + $this->taxCalculator = $this->createMock(TaxCalculator::class); + + $this->taxCalculator->method('addTaxes')->willReturn(11.0); + $this->taxCalculator->method('getTotalRate')->willReturn(10); + + $this->taxCalculatorProvider->method('getTaxCalculator')->willReturn($this->taxCalculator); + + $this->context->method('getCustomerAddressInvoiceId')->willReturn(1); + $this->context->method('getComputingPrecision')->willReturn(2); + + $paymentFeeProvider = new PaymentFeeProvider( + $this->context, + $this->addressRepository, + $this->taxCalculatorProvider + ); + + $result = $paymentFeeProvider->getPaymentFee($this->molPaymentMethod, 10); + + $this->assertEquals(11.0, $result->getPaymentFeeTaxIncl()); + $this->assertEquals(10.0, $result->getPaymentFeeTaxExcl()); + $this->assertEquals(10.0, $result->getTaxRate()); + $this->assertEquals(true, $result->isActive()); + } + + public function testItSuccessfullyProvidesPercentagePaymentFee(): void + { + $this->molPaymentMethod->surcharge = Config::FEE_PERCENTAGE; + $this->molPaymentMethod->surcharge_percentage = 10; + $this->molPaymentMethod->surcharge_limit = 10; + $this->molPaymentMethod->surcharge_fixed_amount_tax_excl = 0; + $this->molPaymentMethod->tax_rules_group_id = 1; + + $this->address->id = 1; + $this->address->id_country = 1; + $this->address->id_state = 0; + + $this->addressRepository->method('findOneBy')->willReturn($this->address); + + $this->taxCalculator->method('removeTaxes')->willReturn(0.9); + $this->taxCalculator->method('getTotalRate')->willReturn(10); + + $this->taxCalculatorProvider->method('getTaxCalculator')->willReturn($this->taxCalculator); + + $this->context->method('getCustomerAddressInvoiceId')->willReturn(1); + $this->context->method('getComputingPrecision')->willReturn(2); + + $paymentFeeProvider = new PaymentFeeProvider( + $this->context, + $this->addressRepository, + $this->taxCalculatorProvider + ); + + $result = $paymentFeeProvider->getPaymentFee($this->molPaymentMethod, 10); + + $this->assertEquals(1.0, $result->getPaymentFeeTaxIncl()); + $this->assertEquals(0.9, $result->getPaymentFeeTaxExcl()); + $this->assertEquals(10.0, $result->getTaxRate()); + $this->assertEquals(true, $result->isActive()); + } + + public function testItSuccessfullyProvidesPercentageAndFixedPricePaymentFee(): void + { + $this->molPaymentMethod->surcharge = Config::FEE_FIXED_FEE_AND_PERCENTAGE; + $this->molPaymentMethod->surcharge_percentage = 10; + $this->molPaymentMethod->surcharge_limit = 100; + $this->molPaymentMethod->surcharge_fixed_amount_tax_excl = 10; + $this->molPaymentMethod->tax_rules_group_id = 1; + + $this->address->id = 1; + $this->address->id_country = 1; + $this->address->id_state = 0; + + $this->addressRepository->method('findOneBy')->willReturn($this->address); + + $this->taxCalculator->method('addTaxes')->willReturn(11.0); + $this->taxCalculator->method('removeTaxes')->willReturn(10.8); + $this->taxCalculator->method('getTotalRate')->willReturn(10); + + $this->taxCalculatorProvider->method('getTaxCalculator')->willReturn($this->taxCalculator); + + $this->context->method('getCustomerAddressInvoiceId')->willReturn(1); + $this->context->method('getComputingPrecision')->willReturn(2); + + $paymentFeeProvider = new PaymentFeeProvider( + $this->context, + $this->addressRepository, + $this->taxCalculatorProvider + ); + + $result = $paymentFeeProvider->getPaymentFee($this->molPaymentMethod, 10); + + $this->assertEquals(12.0, $result->getPaymentFeeTaxIncl()); + $this->assertEquals(10.8, $result->getPaymentFeeTaxExcl()); + $this->assertEquals(10.0, $result->getTaxRate()); + $this->assertEquals(true, $result->isActive()); + } + + public function testItSuccessfullyProvidesFeeSurchageTypeNotFound(): void + { + $this->molPaymentMethod->surcharge = 999; + $this->molPaymentMethod->surcharge_percentage = 0; + $this->molPaymentMethod->surcharge_limit = 0; + $this->molPaymentMethod->surcharge_fixed_amount_tax_excl = 0; + $this->molPaymentMethod->tax_rules_group_id = 0; + + $this->address->id = 1; + $this->address->id_country = 1; + $this->address->id_state = 0; + + $this->addressRepository->method('findOneBy')->willReturn($this->address); + + $this->taxCalculatorProvider->method('getTaxCalculator')->willReturn($this->taxCalculator); + + $this->context->method('getCustomerAddressInvoiceId')->willReturn(1); + $this->context->method('getComputingPrecision')->willReturn(2); + + $paymentFeeProvider = new PaymentFeeProvider( + $this->context, + $this->addressRepository, + $this->taxCalculatorProvider + ); + + $result = $paymentFeeProvider->getPaymentFee($this->molPaymentMethod, 10); + + $this->assertEquals(0.0, $result->getPaymentFeeTaxIncl()); + $this->assertEquals(0.0, $result->getPaymentFeeTaxExcl()); + $this->assertEquals(0.0, $result->getTaxRate()); + $this->assertEquals(false, $result->isActive()); + } + + public function testItUnsuccessfullyProvidesFeeAddressNotFound(): void + { + $this->molPaymentMethod->surcharge_percentage = 0; + $this->molPaymentMethod->surcharge_limit = 0; + $this->molPaymentMethod->surcharge_fixed_amount_tax_excl = 0; + + $this->addressRepository->method('findOneBy')->willReturn(null); + + $paymentFeeProvider = new PaymentFeeProvider( + $this->context, + $this->addressRepository, + $this->taxCalculatorProvider + ); + + $this->expectException(FailedToProvidePaymentFeeException::class); + $this->expectExceptionCode(ExceptionCode::FAILED_TO_FIND_CUSTOMER_ADDRESS); + + $paymentFeeProvider->getPaymentFee($this->molPaymentMethod, 10); + } +} diff --git a/tests/Unit/Provider/SubscriptionIntervalTest.php b/tests/Unit/Provider/SubscriptionIntervalTest.php index cf6ecda3d..eb819f3a4 100644 --- a/tests/Unit/Provider/SubscriptionIntervalTest.php +++ b/tests/Unit/Provider/SubscriptionIntervalTest.php @@ -18,6 +18,7 @@ public function testGetSubscriptionInterval(array $attributeId, array $mockedGet { $configurationMock = $this->createMock(ConfigurationAdapter::class); $configurationMock + ->expects($this->any()) ->method('get') ->will( $this->returnValueMap($mockedGetResults) @@ -37,6 +38,9 @@ public function testGetSubscriptionInterval(array $attributeId, array $mockedGet public function descriptionDataProvider(): array { $langId = null; + $shopGroupId = null; + $shopId = null; + $dailyProductAttributeId = 1; $weeklyProductAttributeId = 2; $monthlyProductAttributeId = 3; @@ -50,9 +54,9 @@ public function descriptionDataProvider(): array ], ], 'mocked get result' => [ - [Config::SUBSCRIPTION_ATTRIBUTE_DAILY, $langId, $dailyProductAttributeId], - [Config::SUBSCRIPTION_ATTRIBUTE_WEEKLY, $langId, $weeklyProductAttributeId], - [Config::SUBSCRIPTION_ATTRIBUTE_MONTHLY, $langId, $monthlyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_DAILY, $langId, $shopGroupId, $shopId, $dailyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_WEEKLY, $langId, $shopGroupId, $shopId, $weeklyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_MONTHLY, $langId, $shopGroupId, $shopId, $monthlyProductAttributeId], ], 'expected result' => Config::getSubscriptionIntervals()[Config::SUBSCRIPTION_ATTRIBUTE_DAILY], ], @@ -63,9 +67,9 @@ public function descriptionDataProvider(): array ], ], 'mocked get result' => [ - [Config::SUBSCRIPTION_ATTRIBUTE_DAILY, $langId, $dailyProductAttributeId], - [Config::SUBSCRIPTION_ATTRIBUTE_WEEKLY, $langId, $weeklyProductAttributeId], - [Config::SUBSCRIPTION_ATTRIBUTE_MONTHLY, $langId, $monthlyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_DAILY, $langId, $shopGroupId, $shopId, $dailyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_WEEKLY, $langId, $shopGroupId, $shopId, $weeklyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_MONTHLY, $langId, $shopGroupId, $shopId, $monthlyProductAttributeId], ], 'expected result' => Config::getSubscriptionIntervals()[Config::SUBSCRIPTION_ATTRIBUTE_WEEKLY], ], @@ -75,9 +79,9 @@ public function descriptionDataProvider(): array ['id' => $basicProductAttribute], ], 'mocked get result' => [ - [Config::SUBSCRIPTION_ATTRIBUTE_DAILY, $langId, $dailyProductAttributeId], - [Config::SUBSCRIPTION_ATTRIBUTE_WEEKLY, $langId, $weeklyProductAttributeId], - [Config::SUBSCRIPTION_ATTRIBUTE_MONTHLY, $langId, $monthlyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_DAILY, $langId, $shopGroupId, $shopId, $dailyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_WEEKLY, $langId, $shopGroupId, $shopId, $weeklyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_MONTHLY, $langId, $shopGroupId, $shopId, $monthlyProductAttributeId], ], 'expected result' => Config::getSubscriptionIntervals()[Config::SUBSCRIPTION_ATTRIBUTE_MONTHLY], ], @@ -86,9 +90,9 @@ public function descriptionDataProvider(): array ['id' => $basicProductAttribute], ], 'mocked get result' => [ - [Config::SUBSCRIPTION_ATTRIBUTE_DAILY, $langId, $dailyProductAttributeId], - [Config::SUBSCRIPTION_ATTRIBUTE_WEEKLY, $langId, $weeklyProductAttributeId], - [Config::SUBSCRIPTION_ATTRIBUTE_MONTHLY, $langId, $monthlyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_DAILY, $langId, $shopGroupId, $shopId, $dailyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_WEEKLY, $langId, $shopGroupId, $shopId, $weeklyProductAttributeId], + [Config::SUBSCRIPTION_ATTRIBUTE_MONTHLY, $langId, $shopGroupId, $shopId, $monthlyProductAttributeId], ], 'expected result' => null, ], diff --git a/tests/Unit/Provider/TaxCalculatorProviderTest.php b/tests/Unit/Provider/TaxCalculatorProviderTest.php new file mode 100644 index 000000000..9d0c941e4 --- /dev/null +++ b/tests/Unit/Provider/TaxCalculatorProviderTest.php @@ -0,0 +1,78 @@ +taxRuleRepository = $this->createMock(TaxRuleRepositoryInterface::class); + $this->taxRepository = $this->createMock(TaxRepositoryInterface::class); + } + + public function testItSuccessfullyProvidesTaxCalculator(): void + { + $taxRule = ['id_tax' => 1, 'behavior' => 0]; + + $tax = $this->createMock(Tax::class); + + $tax->id = 1; + $tax->rate = 10; + + $this->taxRuleRepository->method('getTaxRule')->willReturn([$taxRule]); + $this->taxRepository->method('findOneBy')->willReturn($tax); + + $taxProvider = new TaxCalculatorProvider( + $this->taxRuleRepository, + $this->taxRepository + ); + + $result = $taxProvider->getTaxCalculator(1, 1, 1); + + $this->assertEquals(10.00, $result->getTotalRate()); + } + + public function testItUnsuccessfullyProvidesTaxCalculatorFailedToFindTaxRule(): void + { + $this->taxRuleRepository->method('getTaxRule')->willReturn([]); + + $taxProvider = new TaxCalculatorProvider( + $this->taxRuleRepository, + $this->taxRepository + ); + + $result = $taxProvider->getTaxCalculator(1, 1, 1); + + $this->assertEquals(0.00, $result->getTotalRate()); + } + + public function testItUnsuccessfullyProvidesTaxCalculatorFailedToFindTax(): void + { + $taxRule = ['id_tax' => 1, 'behavior' => 0]; + + $this->taxRuleRepository->method('getTaxRule')->willReturn([$taxRule]); + $this->taxRepository->method('findOneBy')->willReturn(null); + + $taxProvider = new TaxCalculatorProvider( + $this->taxRuleRepository, + $this->taxRepository + ); + + $result = $taxProvider->getTaxCalculator(1, 1, 1); + + $this->assertEquals(0.00, $result->getTotalRate()); + } +} diff --git a/tests/Unit/Service/CartLinesServiceTest.php b/tests/Unit/Service/CartLinesServiceTest.php index 96e3c8f84..1d80eba79 100644 --- a/tests/Unit/Service/CartLinesServiceTest.php +++ b/tests/Unit/Service/CartLinesServiceTest.php @@ -13,13 +13,14 @@ namespace Service; use Mollie\Adapter\ConfigurationAdapter; +use Mollie\Adapter\Context; use Mollie\Adapter\ToolsAdapter; use Mollie\DTO\Line; use Mollie\DTO\Object\Amount; +use Mollie\DTO\PaymentFeeData; use Mollie\Service\CartLinesService; use Mollie\Service\LanguageService; use Mollie\Service\VoucherService; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class CartLinesServiceTest extends TestCase @@ -54,27 +55,29 @@ public function testGetCartLines( $mocks, $result ) { - /** @var MockObject $configurationAdapter */ $configurationAdapter = $this->getMockBuilder(ConfigurationAdapter::class)->getMock(); + foreach ($mocks as $mock) { $configurationAdapter->expects(self::at($mock['at']))->method($mock['function'])->with($mock['expects'])->willReturn($mock['return']); } - /** @var MockObject $languageService */ $languageService = $this->getMockBuilder(LanguageService::class)->disableOriginalConstructor()->getMock(); + foreach ($translationMocks as $mock) { $languageService->expects(self::at($mock['at']))->method($mock['function'])->with($mock['expects'])->willReturn($mock['return']); } - /** @var ToolsAdapter $toolsAdapter */ $toolsAdapter = $this->getMockBuilder(ToolsAdapter::class)->getMock(); + foreach ($toolsMocks as $mock) { $toolsAdapter->method($mock['function'])->with($mock['expects'])->willReturn($mock['return']); } - $voucherService = new VoucherService($configurationAdapter); + $voucherService = $this->getMockBuilder(VoucherService::class)->disableOriginalConstructor()->getMock(); + $context = $this->getMockBuilder(Context::class)->getMock(); + + $cartLineService = new CartLinesService($languageService, $voucherService, $toolsAdapter, $context); - $cartLineService = new CartLinesService($languageService, $voucherService, $toolsAdapter); $cartLines = $cartLineService->getCartLines( $amount, $paymentFee, @@ -101,7 +104,7 @@ public function cartLinesProvider() return [ 'two products with a gift which is the same as one product' => [ 'amount' => 204.84, - 'paymentFee' => false, + 'paymentFee' => new PaymentFeeData(0.00, 0.00, 0.00, false), 'currencyIsoCode' => $currencyIsoCode, 'cartSummary' => [ 'gift_products' => [ @@ -134,6 +137,8 @@ public function cartLinesProvider() 'id_product_attribute' => '9', 'id_customization' => null, 'features' => [], + 'link_rewrite' => 'test-link', + 'id_image' => 'test-image-id', ], 1 => [ 'total_wt' => 100, @@ -145,6 +150,8 @@ public function cartLinesProvider() 'id_product_attribute' => '9', 'id_customization' => null, 'features' => [], + 'link_rewrite' => 'test-link', + 'id_image' => 'test-image-id', ], ], 'psGiftWrapping' => '1', @@ -211,7 +218,7 @@ public function cartLinesProvider() ], 'one products with a gift' => [ 'amount' => 104.84, - 'paymentFee' => false, + 'paymentFee' => new PaymentFeeData(0.00, 0.00, 0.00, false), 'currencyIsoCode' => $currencyIsoCode, 'cartSummary' => [ 'gift_products' => [ @@ -244,6 +251,8 @@ public function cartLinesProvider() 'id_product_attribute' => '9', 'id_customization' => null, 'features' => [], + 'link_rewrite' => 'test-link', + 'id_image' => 'test-image-id', ], 1 => [ 'total_wt' => 100, @@ -255,6 +264,8 @@ public function cartLinesProvider() 'id_product_attribute' => '9', 'id_customization' => null, 'features' => [], + 'link_rewrite' => 'test-link', + 'id_image' => 'test-image-id', ], ], 'psGiftWrapping' => '1', @@ -311,7 +322,7 @@ public function cartLinesProvider() ], 'product without name' => [ 'amount' => 104.84, - 'paymentFee' => false, + 'paymentFee' => new PaymentFeeData(0.00, 0.00, 0.00, false), 'currencyIsoCode' => $currencyIsoCode, 'cartSummary' => [ 'gift_products' => [ @@ -344,6 +355,8 @@ public function cartLinesProvider() 'id_product_attribute' => '9', 'id_customization' => null, 'features' => [], + 'link_rewrite' => 'test-link', + 'id_image' => 'test-image-id', ], ], 'psGiftWrapping' => '1', @@ -390,7 +403,7 @@ public function cartLinesProvider() ], 'Cart with discount' => [ 'amount' => 98.79, - 'paymentFee' => false, + 'paymentFee' => new PaymentFeeData(0.00, 0.00, 0.00, false), 'currencyIsoCode' => $currencyIsoCode, 'cartSummary' => [ 'gift_products' => [ @@ -419,6 +432,8 @@ public function cartLinesProvider() 'id_product_attribute' => '9', 'id_customization' => null, 'features' => [], + 'link_rewrite' => 'test-link', + 'id_image' => 'test-image-id', ], ], 'psGiftWrapping' => '1', diff --git a/tests/Unit/Verification/Shipment/CanSendShipmentTest.php b/tests/Unit/Verification/Shipment/CanSendShipmentTest.php index 0086f2b46..f309de0d8 100644 --- a/tests/Unit/Verification/Shipment/CanSendShipmentTest.php +++ b/tests/Unit/Verification/Shipment/CanSendShipmentTest.php @@ -3,6 +3,7 @@ use Mollie\Config\Config; use Mollie\Enum\PaymentTypeEnum; use Mollie\Exception\ShipmentCannotBeSentException; +use Mollie\Verification\IsPaymentInformationAvailable; use PHPUnit\Framework\TestCase; class CanSendShipmentTest extends TestCase @@ -42,6 +43,9 @@ class CanSendShipmentTest extends TestCase */ private $orderState; + /** @var \Mollie\Verification\isPaymentInformationAvailable|\PHPUnit\Framework\MockObject\MockObject */ + private $isPaymentInformationAvailable; + protected function setUp(): void { parent::setUp(); @@ -87,6 +91,12 @@ protected function setUp(): void ->disableOriginalConstructor() ->getMock() ; + + $this->isPaymentInformationAvailable = $this + ->getMockBuilder(IsPaymentInformationAvailable::class) + ->disableOriginalConstructor() + ->getMock() + ; } /** @dataProvider getSendShipmentVerificationData */ @@ -95,6 +105,7 @@ public function testCanSendShipment( $configuration, $automaticShipmentSenderStatuses, $paymentInformation, + $paymentInformationAvailable, $paymentType, $exception, $expected @@ -136,18 +147,26 @@ public function testCanSendShipment( ->willReturn($paymentType) ; + $this->isPaymentInformationAvailable + ->expects($this->any()) + ->method('verify') + ->willReturn($paymentInformationAvailable) + ; + $canSendShipment = new \Mollie\Verification\Shipment\CanSendShipment( $this->configurationAdapter, $this->automaticShipmentSenderStatusesProvider, $this->orderEndpointPaymentTypeHandler, $this->paymentMethodRepository, - $this->shipmentService + $this->shipmentService, + $this->isPaymentInformationAvailable ); if ($exception) { $this->expectException($exception['class']); $this->expectExceptionCode($exception['code']); } + $result = $canSendShipment->verify($this->order, $this->orderState); $this->assertEquals($expected, $result); @@ -167,6 +186,7 @@ public function getSendShipmentVerificationData() 'paymentInformation' => [ 'transaction_id' => 'test', ], + 'paymentInformationAvailable' => true, 'paymentType' => PaymentTypeEnum::PAYMENT_TYPE_ORDER, 'exception' => [], 'expected' => true, @@ -180,6 +200,7 @@ public function getSendShipmentVerificationData() 'paymentInformation' => [ 'transaction_id' => 'test', ], + 'paymentInformationAvailable' => true, 'paymentType' => PaymentTypeEnum::PAYMENT_TYPE_ORDER, 'exception' => [], 'expected' => true, @@ -195,12 +216,10 @@ public function getSendShipmentVerificationData() 'paymentInformation' => [ 'transaction_id' => 'test', ], + 'paymentInformationAvailable' => true, 'paymentType' => PaymentTypeEnum::PAYMENT_TYPE_ORDER, - 'exception' => [ - 'class' => ShipmentCannotBeSentException::class, - 'code' => ShipmentCannotBeSentException::AUTOMATIC_SHIPMENT_SENDER_IS_NOT_AVAILABLE, - ], - 'expected' => null, + 'exception' => [], + 'expected' => false, ], 'Order state is not in automatic shipment sender list' => [ 'shipmentInformation' => [ @@ -213,12 +232,10 @@ public function getSendShipmentVerificationData() 'paymentInformation' => [ 'transaction_id' => 'test', ], + 'paymentInformationAvailable' => true, 'paymentType' => PaymentTypeEnum::PAYMENT_TYPE_ORDER, - 'exception' => [ - 'class' => ShipmentCannotBeSentException::class, - 'code' => ShipmentCannotBeSentException::AUTOMATIC_SHIPMENT_SENDER_IS_NOT_AVAILABLE, - ], - 'expected' => null, + 'exception' => [], + 'expected' => false, ], 'Has no payment information' => [ 'shipmentInformation' => [ @@ -229,6 +246,7 @@ public function getSendShipmentVerificationData() ], 'automaticShipmentSenderStatuses' => [0, 1, 2, 3], 'paymentInformation' => [], + 'paymentInformationAvailable' => false, 'paymentType' => PaymentTypeEnum::PAYMENT_TYPE_ORDER, 'exception' => [ 'class' => ShipmentCannotBeSentException::class, @@ -247,6 +265,7 @@ public function getSendShipmentVerificationData() 'paymentInformation' => [ 'test' => 123, ], + 'paymentInformationAvailable' => false, 'paymentType' => PaymentTypeEnum::PAYMENT_TYPE_ORDER, 'exception' => [ 'class' => ShipmentCannotBeSentException::class, @@ -265,6 +284,7 @@ public function getSendShipmentVerificationData() 'paymentInformation' => [ 'transaction_id' => 'test', ], + 'paymentInformationAvailable' => true, 'paymentType' => PaymentTypeEnum::PAYMENT_TYPE_PAYMENT, 'exception' => [ 'class' => ShipmentCannotBeSentException::class, diff --git a/tests/phpstan/phpstan_base.neon b/tests/phpstan/phpstan_base.neon index 6fec945ec..b631d69ca 100644 --- a/tests/phpstan/phpstan_base.neon +++ b/tests/phpstan/phpstan_base.neon @@ -39,5 +39,7 @@ parameters: - '#Access to property \$[a-zA-Z0-9_]+ on an unknown class AttributeCore.#' - '#Call to method [a-zA-Z0-9_]+\(\) on an unknown class ProductAttributeCore.#' - '#Call to method [a-zA-Z0-9_]+\(\) on an unknown class AttributeCore.#' + - '#Parameter \#1 \$value of method ControllerCore\:\:ajaxRender\(\) expects null, string\|false given.#' + - '#Call to function is_subclass_of\(\) with.*will always evaluate to false.#' level: 5 diff --git a/upgrade/Upgrade-4.0.8.php b/upgrade/Upgrade-4.0.8.php index a061b79b9..32d05c1ce 100644 --- a/upgrade/Upgrade-4.0.8.php +++ b/upgrade/Upgrade-4.0.8.php @@ -22,10 +22,13 @@ * @throws PrestaShopDatabaseException * @throws PrestaShopException */ -function upgrade_module_4_0_8() +function upgrade_module_4_0_8(Mollie $module) { - Configuration::updateValue(Config::MOLLIE_SINGLE_CLICK_PAYMENT, 0); - Configuration::updateValue(Config::MOLLIE_ENVIRONMENT, Config::ENVIRONMENT_LIVE); + /** @var \Mollie\Adapter\ConfigurationAdapter $configuration */ + $configuration = $module->getService(\Mollie\Adapter\ConfigurationAdapter::class); + + $configuration->updateValue(Config::MOLLIE_ENVIRONMENT, Config::ENVIRONMENT_LIVE); + $configuration->updateValue(Config::MOLLIE_SINGLE_CLICK_PAYMENT, 0); $sql = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'mol_customer` ( `id_mol_customer` INT(64) NOT NULL PRIMARY KEY AUTO_INCREMENT, diff --git a/upgrade/Upgrade-6.0.0.php b/upgrade/Upgrade-6.0.0.php index 3da573dc5..6b3444541 100644 --- a/upgrade/Upgrade-6.0.0.php +++ b/upgrade/Upgrade-6.0.0.php @@ -34,18 +34,8 @@ function upgrade_module_6_0_0(Mollie $module): bool } } - //todo: maybe move to container - $installer = new \Mollie\Install\Installer( - $module, - new \Mollie\Service\OrderStateImageService(), - new \Mollie\Install\DatabaseTableInstaller(), - new \Mollie\Tracker\Segment( - new \Mollie\Adapter\Shop(), - new \Mollie\Adapter\Language(), - new \Mollie\Config\Env() - ), - new \Mollie\Adapter\ConfigurationAdapter() - ); + /** @var Mollie\Install\Installer $installer */ + $installer = $module->getService(Mollie\Install\Installer::class); $installer->installSpecificTabs(); diff --git a/upgrade/Upgrade-6.0.1.php b/upgrade/Upgrade-6.0.1.php new file mode 100644 index 000000000..d3566b40e --- /dev/null +++ b/upgrade/Upgrade-6.0.1.php @@ -0,0 +1,131 @@ + + * @copyright Mollie B.V. + * @license https://github.com/mollie/PrestaShop/blob/master/LICENSE.md + * + * @see https://github.com/mollie/PrestaShop + */ + +use Mollie\Adapter\ConfigurationAdapter; +use Mollie\Config\Config; + +if (!defined('_PS_VERSION_')) { + exit; +} + +function upgrade_module_6_0_1(Mollie $module): bool +{ + /** @var ConfigurationAdapter $configuration */ + $configuration = $module->getService(ConfigurationAdapter::class); + + $configuration->updateValue(Config::MOLLIE_IFRAME['production'], Configuration::get('MOLLIE_IFRAME')); + $configuration->updateValue(Config::MOLLIE_IFRAME['sandbox'], Configuration::get('MOLLIE_IFRAME')); + + $configuration->updateValue(Config::MOLLIE_ISSUERS['production'], Configuration::get('MOLLIE_ISSUERS')); + $configuration->updateValue(Config::MOLLIE_ISSUERS['sandbox'], Configuration::get('MOLLIE_ISSUERS')); + + $configuration->updateValue(Config::MOLLIE_SINGLE_CLICK_PAYMENT['production'], Configuration::get('MOLLIE_SINGLE_CLICK_PAYMENT')); + $configuration->updateValue(Config::MOLLIE_SINGLE_CLICK_PAYMENT['sandbox'], Configuration::get('MOLLIE_SINGLE_CLICK_PAYMENT')); + + if (!modifyExistingTables601()) { + return false; + } + + return true; +} + +function modifyExistingTables601(): bool +{ + $sql = ' + SELECT COUNT(*) > 0 AS count + FROM information_schema.columns + WHERE TABLE_SCHEMA = "' . _DB_NAME_ . '" AND table_name = "' . _DB_PREFIX_ . 'mol_payment_method" AND column_name = "tax_rules_group_id"; + '; + + /** only add it if it doesn't exist */ + if (!(int) Db::getInstance()->getValue($sql)) { + $sql = ' + ALTER TABLE ' . _DB_PREFIX_ . 'mol_payment_method + CHANGE surcharge_fixed_amount surcharge_fixed_amount_tax_excl decimal(20,6), + ADD COLUMN tax_rules_group_id int(10) DEFAULT 0; + '; + + try { + if (!Db::getInstance()->execute($sql)) { + return false; + } + } catch (Exception $e) { + PrestaShopLogger::addLog("Mollie upgrade error: {$e->getMessage()}"); + + return false; + } + } + + $sql = ' + SELECT COUNT(*) > 0 AS count + FROM information_schema.columns + WHERE TABLE_SCHEMA = "' . _DB_NAME_ . '" AND table_name = "' . _DB_PREFIX_ . 'mol_order_payment_fee"; + '; + + /** only add it if it doesn't exist */ + if (!(int) Db::getInstance()->getValue($sql)) { + $sql = 'ALTER TABLE ' . _DB_PREFIX_ . 'mol_order_fee MODIFY id_mol_order_fee INT(64)'; + + try { + if (!Db::getInstance()->execute($sql)) { + return false; + } + } catch (Exception $e) { + PrestaShopLogger::addLog("Mollie upgrade error: {$e->getMessage()}"); + + return false; + } + + $sql = 'ALTER TABLE ' . _DB_PREFIX_ . 'mol_order_fee DROP PRIMARY KEY'; + + try { + if (!Db::getInstance()->execute($sql)) { + return false; + } + } catch (Exception $e) { + PrestaShopLogger::addLog("Mollie upgrade error: {$e->getMessage()}"); + + return false; + } + + $sql = ' + ALTER TABLE ' . _DB_PREFIX_ . 'mol_order_fee + CHANGE order_fee fee_tax_incl decimal(20,6) NOT NULL, + CHANGE id_mol_order_fee id_mol_order_payment_fee INT AUTO_INCREMENT PRIMARY KEY, + ADD COLUMN id_order INT(64) NOT NULL, + ADD COLUMN fee_tax_excl decimal(20,6) NOT NULL; + '; + + try { + if (!Db::getInstance()->execute($sql)) { + return false; + } + } catch (Exception $e) { + PrestaShopLogger::addLog("Mollie upgrade error: {$e->getMessage()}"); + + return false; + } + + $sql = 'RENAME TABLE ' . _DB_PREFIX_ . 'mol_order_fee TO ' . _DB_PREFIX_ . 'mol_order_payment_fee'; + + try { + if (!Db::getInstance()->execute($sql)) { + return false; + } + } catch (Exception $e) { + PrestaShopLogger::addLog("Mollie upgrade error: {$e->getMessage()}"); + + return false; + } + } + + return true; +} diff --git a/views/js/admin/payment_methods.js b/views/js/admin/payment_methods.js index 7a46ec464..9b028a118 100644 --- a/views/js/admin/payment_methods.js +++ b/views/js/admin/payment_methods.js @@ -47,4 +47,107 @@ $(document).ready(function() { }); $('input[name="activateModule"]').parent('div').hide(); + + let typingTimer; + let doneTypingInterval = 500; + + $(document).on('keyup', + 'input[name^="' + paymentMethodSurchargeFixedAmountTaxInclConfig + '"],' + + 'input[name^="' + paymentMethodSurchargeFixedAmountTaxExclConfig + '"]', + function () { + clearTimeout(typingTimer); + + const inputValue = this.value; + const inputElement = this; + + if (inputValue) { + typingTimer = setTimeout(function () { + let inputName = $(inputElement).attr('name'); + + let paymentFeeTaxIncl = 0.00; + let paymentFeeTaxExcl = 0.00; + + if (inputName.indexOf(paymentMethodSurchargeFixedAmountTaxInclConfig) >= 0) { + paymentFeeTaxIncl = inputValue; + } else { + paymentFeeTaxExcl = inputValue; + } + + const $paymentMethod = $(inputElement.closest('div[id^="payment-method-form-"]')); + + if ($paymentMethod.length < 1) { + console.error('Failed to find payment form parent element'); + + return; + } + + let taxRulesGroupId = $paymentMethod.find('select[name^="' + paymentMethodTaxRulesGroupIdConfig + '"]').val(); + + updatePaymentFee($paymentMethod, paymentFeeTaxIncl, paymentFeeTaxExcl, taxRulesGroupId); + }, doneTypingInterval) + } + }); + + $(document).on('change', + 'select[name^="' + paymentMethodTaxRulesGroupIdConfig + '"]', + function () { + const taxRulesGroupId = this.value; + const inputElement = this; + + const $paymentMethod = $(inputElement.closest('div[id^="payment-method-form-"]')); + + if ($paymentMethod.length < 1) { + console.error('Failed to find payment form parent element'); + + return; + } + + let paymentFeeTaxIncl = 0.00; + let $paymentFeeTaxExcl = $paymentMethod.find('input[name^="' + paymentMethodSurchargeFixedAmountTaxExclConfig + '"]'); + + if ($paymentFeeTaxExcl.length < 1) { + console.error('Failed to find payment fee tax excluded price'); + + return; + } + + let paymentFeeTaxExcl = $paymentFeeTaxExcl.val(); + + updatePaymentFee($paymentMethod, paymentFeeTaxIncl, paymentFeeTaxExcl, taxRulesGroupId); + }); + + function updatePaymentFee($paymentMethod, paymentFeeTaxIncl, paymentFeeTaxExcl, taxRulesGroupId) { + $.ajax(ajaxUrl, { + method: 'POST', + data: { + 'action': 'updateFixedPaymentFeePrice', + 'paymentFeeTaxIncl': paymentFeeTaxIncl, + 'paymentFeeTaxExcl': paymentFeeTaxExcl, + 'taxRulesGroupId': taxRulesGroupId, + 'ajax': 1, + }, + success: function (response) { + response = JSON.parse(response); + + if (response.error) { + console.error(response.message) + + return; + } + + let $paymentFeeTaxIncl = $paymentMethod.find('input[name^="' + paymentMethodSurchargeFixedAmountTaxInclConfig + '"]'); + let $paymentFeeTaxExcl = $paymentMethod.find('input[name^="' + paymentMethodSurchargeFixedAmountTaxExclConfig + '"]'); + + if ($paymentFeeTaxIncl.length < 1 || $paymentFeeTaxExcl.length < 1) { + console.error('Failed to find payment fee input'); + + return; + } + + $paymentFeeTaxIncl.val(response.paymentFeeTaxIncl); + $paymentFeeTaxExcl.val(response.paymentFeeTaxExcl); + } + } + ) + } }) diff --git a/views/js/front/payment_fee.js b/views/js/front/payment_fee.js index fc4040a59..eac3d2c57 100644 --- a/views/js/front/payment_fee.js +++ b/views/js/front/payment_fee.js @@ -33,24 +33,38 @@ $(document).ready(function () { } $('input[name="payment-option"]').on('change', function () { - var $nextDiv = $(this).closest('.payment-option').parent().next(); - var paymentFee; + const $nextDiv = $(this).closest('.payment-option').parent().next(); + + let paymentMethodId = 0; + let $paymentMethodId; + if ($nextDiv.hasClass('js-payment-option-form')) { - paymentFee = $nextDiv.find('input[name="payment-fee-price"]').val(); + $paymentMethodId = $nextDiv.find('input[name="payment-method-id"]'); } else { - paymentFee = $nextDiv.next().find('input[name="payment-fee-price"]').val(); + $paymentMethodId = $nextDiv.next().find('input[name="payment-method-id"]'); + } + + if ($paymentMethodId.length > 0) { + paymentMethodId = $paymentMethodId.val(); } $.ajax({ url: ajaxUrl, method: 'GET', data: { - 'paymentFee': paymentFee, + paymentMethodId: paymentMethodId, ajax: 1, action: 'getTotalCartPrice' }, success: function (response) { response = jQuery.parseJSON(response); + + if (response.error) { + console.error(response.message); + + return; + } + $('.card-block.cart-summary-totals').replaceWith(response.cart_summary_totals); } }) diff --git a/views/js/validation.js b/views/js/validation.js index 8837683cf..dfe8e6156 100644 --- a/views/js/validation.js +++ b/views/js/validation.js @@ -132,29 +132,40 @@ $(document).ready(function () { } function paymentMethodFeeToggle(method) { - var $paymentMethodForm = $(method).closest('.payment-method'); - var $paymentFeeType = $paymentMethodForm.find('select[name^="MOLLIE_METHOD_SURCHARGE_TYPE"]'); - var $feeFixed = $paymentMethodForm.find('input[name^="MOLLIE_METHOD_SURCHARGE_FIXED_AMOUNT"]'); - var $feePercentage = $paymentMethodForm.find('input[name^="MOLLIE_METHOD_SURCHARGE_PERCENTAGE"]'); - var $feeLimit = $paymentMethodForm.find('input[name^="MOLLIE_METHOD_SURCHARGE_LIMIT"]'); + let $paymentMethodForm = $(method).closest('.payment-method'); + let $paymentFeeType = $paymentMethodForm.find('select[name^="MOLLIE_METHOD_SURCHARGE_TYPE"]'); + let $feeFixedTaxIncl = $paymentMethodForm.find('input[name^="' + paymentMethodSurchargeFixedAmountTaxInclConfig + '"]'); + let $feeFixedTaxExcl = $paymentMethodForm.find('input[name^="' + paymentMethodSurchargeFixedAmountTaxExclConfig + '"]'); + let $taxRulesGroupId = $paymentMethodForm.find('select[name^="' + paymentMethodTaxRulesGroupIdConfig + '"]'); + let $feePercentage = $paymentMethodForm.find('input[name^="MOLLIE_METHOD_SURCHARGE_PERCENTAGE"]'); + let $feeLimit = $paymentMethodForm.find('input[name^="MOLLIE_METHOD_SURCHARGE_LIMIT"]'); + switch ($paymentFeeType.val()) { case '0': - $feeFixed.closest('.form-group').hide(); + $feeFixedTaxIncl.closest('.form-group').hide(); + $feeFixedTaxExcl.closest('.form-group').hide(); + $taxRulesGroupId.closest('.form-group').hide(); $feePercentage.closest('.form-group').hide(); $feeLimit.closest('.form-group').hide(); break; case '1': - $feeFixed.closest('.form-group').show(); + $feeFixedTaxIncl.closest('.form-group').show(); + $feeFixedTaxExcl.closest('.form-group').show(); + $taxRulesGroupId.closest('.form-group').show(); $feePercentage.closest('.form-group').hide(); $feeLimit.closest('.form-group').hide(); break; case '2': - $feeFixed.closest('.form-group').hide(); + $feeFixedTaxIncl.closest('.form-group').hide(); + $feeFixedTaxExcl.closest('.form-group').hide(); + $taxRulesGroupId.closest('.form-group').show(); $feePercentage.closest('.form-group').show(); $feeLimit.closest('.form-group').show(); break; case '3': - $feeFixed.closest('.form-group').show(); + $feeFixedTaxIncl.closest('.form-group').show(); + $feeFixedTaxExcl.closest('.form-group').show(); + $taxRulesGroupId.closest('.form-group').show(); $feePercentage.closest('.form-group').show(); $feeLimit.closest('.form-group').show(); break; diff --git a/views/templates/admin/_configure/helpers/form/form.tpl b/views/templates/admin/_configure/helpers/form/form.tpl index a4fac9731..b0e310d16 100644 --- a/views/templates/admin/_configure/helpers/form/form.tpl +++ b/views/templates/admin/_configure/helpers/form/form.tpl @@ -294,15 +294,43 @@