diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..cba477d
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,7 @@
+Please refer first to [UPGRADE.md](UPGRADE.md) for the most important items that should be addressed before attempting to upgrade or during the upgrade of a vanilla Oro application.
+
+The current file describes significant changes in the code that may affect the upgrade of your customizations.
+
+## 4.2.0
+
+OroAlternativeCheckoutBundle has been moved from oro/commerce package to oro/commerce-demo-checkouts package.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c57e204
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,7 @@
+OroCommerce
+
+The Open Software License version 3.0
+
+Copyright (c) 2020, Oro, Inc.
+
+Full license is at: http://opensource.org/licenses/OSL-3.0
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..49f46e8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,26 @@
+OroCommerce Checkout Customization Examples
+===========================================
+
+This package includes examples of OroCommerce storefront checkout customizations.
+
+OroAlternativeCheckoutBundle
+----------------------------
+
+OroAlternativeCheckoutBundle adds review and approval steps to the checkout workflow in the OroCommerce storefront if the order amount exceeds the order approval threshold value set by an application administrator in the alternative checkout workflow configuration UI.
+
+Installation
+------------
+
+This package can be added to an existing installation of an OroCommerce application.
+
+Use composer to add the package code:
+
+```
+composer require oro/commerce-demo-checkouts
+```
+
+Perform the installation:
+
+```
+php bin/console oro:platform:update --env=prod
+```
diff --git a/UPGRADE.md b/UPGRADE.md
new file mode 100644
index 0000000..b792154
--- /dev/null
+++ b/UPGRADE.md
@@ -0,0 +1,3 @@
+This file includes only the most important items that should be addressed before attempting to upgrade or during the upgrade of a vanilla Oro application.
+
+Please refer to [CHANGELOG.md](CHANGELOG.md) for a list of significant changes in the code that may affect the upgrade of some customizations.
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..4da54b0
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,32 @@
+{
+ "name": "oro/commerce-demo-checkouts",
+ "description": "OroCommerce checkout customization examples",
+ "homepage": "https://github.com/oroinc/commerce-demo-checkouts",
+ "license": "OSL-3.0",
+ "authors": [
+ {
+ "name": "Oro, Inc",
+ "homepage": "https://www.orocommerce.com"
+ }
+ ],
+ "autoload": {
+ "psr-4": {"Oro\\Bundle\\AlternativeCheckoutBundle\\": "src/Oro/Bundle/AlternativeCheckoutBundle"},
+ "exclude-from-classmap": ["/Tests/"]
+ },
+ "require": {
+ "oro/commerce": "4.2.*"
+ },
+ "repositories": {
+ "oro": {
+ "type": "composer",
+ "url": "https://packagist.oroinc.com"
+ }
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.2-dev"
+ }
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Condition/OrderTotalLimit.php b/src/Oro/Bundle/AlternativeCheckoutBundle/Condition/OrderTotalLimit.php
new file mode 100644
index 0000000..d19b0a9
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Condition/OrderTotalLimit.php
@@ -0,0 +1,68 @@
+totalsProvider = $totalsProvider;
+ $this->checkoutLineItemsManager = $checkoutLineItemsManager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName(): string
+ {
+ return 'less_order_total_limit';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function isConditionAllowed($context): bool
+ {
+ return $this->doCompare(
+ $this->resolveValue($context, $this->left),
+ $this->resolveValue($context, $this->right)
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doCompare($left, $right): bool
+ {
+ $orderLineItems = $this->checkoutLineItemsManager->getData($left);
+ $order = new Order();
+ $order->setLineItems($orderLineItems);
+
+ $orderTotalAmount = $this->totalsProvider->enableRecalculation()->getTotal($order)->getAmount();
+
+ return $orderTotalAmount <= $right;
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/DependencyInjection/OroAlternativeCheckoutExtension.php b/src/Oro/Bundle/AlternativeCheckoutBundle/DependencyInjection/OroAlternativeCheckoutExtension.php
new file mode 100644
index 0000000..7daa18b
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/DependencyInjection/OroAlternativeCheckoutExtension.php
@@ -0,0 +1,24 @@
+load('services.yml');
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/EventListener/QuantityToOrderConditionListener.php b/src/Oro/Bundle/AlternativeCheckoutBundle/EventListener/QuantityToOrderConditionListener.php
new file mode 100644
index 0000000..241a90d
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/EventListener/QuantityToOrderConditionListener.php
@@ -0,0 +1,17 @@
+getRepository('OroWorkflowBundle:WorkflowDefinition')
+ ->find('b2b_flow_alternative_checkout');
+
+ if (!$workflowDefinition) {
+ return;
+ }
+
+ $this->container->get('oro_workflow.manager.system')->activateWorkflow($workflowDefinition->getName());
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Data/Demo/ORM/data/acme_frontend_roles.yml b/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Data/Demo/ORM/data/acme_frontend_roles.yml
new file mode 100644
index 0000000..0ed5dde
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Data/Demo/ORM/data/acme_frontend_roles.yml
@@ -0,0 +1,12 @@
+ACME_BUYER:
+ permissions:
+ action|oro_alternativecheckout_checkout_approve: [NONE]
+ workflow|b2b_flow_alternative_checkout: [VIEW_WORKFLOW_BASIC, PERFORM_TRANSITIONS_BASIC]
+
+ACME_ADMINISTRATOR:
+ permissions:
+ workflow|b2b_flow_alternative_checkout: [VIEW_WORKFLOW_DEEP, PERFORM_TRANSITIONS_DEEP]
+
+ACME_ANONYMOUS:
+ permissions:
+ workflow|b2b_flow_alternative_checkout: [VIEW_WORKFLOW_BASIC, PERFORM_TRANSITIONS_BASIC]
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Data/ORM/UpdateWorkflowsPermissions.php b/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Data/ORM/UpdateWorkflowsPermissions.php
new file mode 100644
index 0000000..ef846ea
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Data/ORM/UpdateWorkflowsPermissions.php
@@ -0,0 +1,30 @@
+ [
+ 'workflow:b2b_flow_alternative_checkout' => ['VIEW_WORKFLOW_DEEP', 'PERFORM_TRANSITIONS_DEEP']
+ ],
+ 'ROLE_FRONTEND_BUYER' => [
+ 'workflow:b2b_flow_alternative_checkout' => ['VIEW_WORKFLOW_BASIC', 'PERFORM_TRANSITIONS_BASIC']
+ ],
+ 'ROLE_FRONTEND_ANONYMOUS' => [
+ 'workflow:b2b_flow_alternative_checkout' => ['VIEW_WORKFLOW_BASIC', 'PERFORM_TRANSITIONS_BASIC']
+ ]
+ ];
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Data/ORM/data/frontend_roles.yml b/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Data/ORM/data/frontend_roles.yml
new file mode 100644
index 0000000..1eb4034
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Data/ORM/data/frontend_roles.yml
@@ -0,0 +1,12 @@
+BUYER:
+ permissions:
+ action|oro_alternativecheckout_checkout_approve: [NONE]
+ workflow|b2b_flow_alternative_checkout: [VIEW_WORKFLOW_BASIC, PERFORM_TRANSITIONS_BASIC]
+
+ADMINISTRATOR:
+ permissions:
+ workflow|b2b_flow_alternative_checkout: [VIEW_WORKFLOW_DEEP, PERFORM_TRANSITIONS_DEEP]
+
+ANONYMOUS:
+ permissions:
+ workflow|b2b_flow_alternative_checkout: [VIEW_WORKFLOW_BASIC, PERFORM_TRANSITIONS_BASIC]
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Schema/OroAlternativeCheckoutBundleInstaller.php b/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Schema/OroAlternativeCheckoutBundleInstaller.php
new file mode 100644
index 0000000..ce30e89
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Migrations/Schema/OroAlternativeCheckoutBundleInstaller.php
@@ -0,0 +1,26 @@
+
+ {% if workflowItem.data.get('allowed') and workflowItem.data.get('allow_request_date') %}
+
+ {{ 'oro.alternativecheckout.messages.approved_at'|trans }} {{ workflowItem.data.get('allow_request_date')|oro_format_datetime }}
+
+ {% else %}
+
+ {{ 'oro.alternativecheckout.messages.waiting_for_approve'|trans }}
+
+ {% endif %}
+ {% if workflowItem.data.get('request_approval_notes') %}
+
+
{{ 'oro.alternativecheckout.requestApprovalNotes.label'|trans }}
+
{{ workflowItem.data.get('request_approval_notes')|nl2br|raw }}
+
+ {% endif %}
+
+ {% if form.state_token is defined %}
+ {{ form_row(form.state_token) }}
+ {% endif %}
+
+ {{ block_widget(block) }}
+
+{%- endblock %}
+
+{% block _checkout_information_widget %}
+ {% set attr = layout_attr_defaults(attr, {
+ '~class': " checkout--no-background"
+ }) %}
+ {{ parent_block_widget(block) }}
+{% endblock %}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Resources/views/layouts/default/oro_checkout_frontend_checkout/templates/request_approval.html.twig b/src/Oro/Bundle/AlternativeCheckoutBundle/Resources/views/layouts/default/oro_checkout_frontend_checkout/templates/request_approval.html.twig
new file mode 100644
index 0000000..ea222d1
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Resources/views/layouts/default/oro_checkout_frontend_checkout/templates/request_approval.html.twig
@@ -0,0 +1,27 @@
+{% block _checkout_form_fields_widget -%}
+
+
+
+ {% set frontendMessage = transitionData.transition.frontendOptions.message %}
+ {{ frontendMessage.content|trans(frontendMessage.message_parameters|default({}), 'workflows')|nl2br }}
+
+
+
+ {{ form_row(form.request_approval_notes, {'attr': {
+ 'placeholder': 'oro.checkout.order_review.note_placeholder'|trans,
+ 'aria-label': 'oro.checkout.order_review.note_aria_label'|trans
+ }}) }}
+
+
+ {{ form_row(form.state_token) }}
+
+ {{ block_widget(block) }}
+
+{%- endblock %}
+
+{% block _checkout_information_widget %}
+ {% set attr = layout_attr_defaults(attr, {
+ '~class': " checkout--no-background"
+ }) %}
+ {{ parent_block_widget(block) }}
+{% endblock %}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/Controller/Frontend/OrderControllerTest.php b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/Controller/Frontend/OrderControllerTest.php
new file mode 100644
index 0000000..e40df0a
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/Controller/Frontend/OrderControllerTest.php
@@ -0,0 +1,280 @@
+initClient(
+ [],
+ $this->generateBasicAuthHeader(LoadCustomerUserData::AUTH_USER, LoadCustomerUserData::AUTH_PW)
+ );
+
+ $this->setCurrentWebsite('default');
+ $this->loadFixtures(
+ [
+ LoadCombinedProductPrices::class,
+ LoadOrders::class,
+ LoadQuoteAlternativeCheckoutsData::class,
+ LoadQuoteAlternativeCheckoutsSubtotalsData::class,
+ LoadShoppingListsCheckoutsData::class,
+ LoadShoppingListLineItems::class
+ ]
+ );
+ }
+
+ public function testCheckoutGrid(): void
+ {
+ $this->client->request('GET', '/'); // any page to authorize a user
+
+ $checkouts = $this->getDatagridData(self::GRID_NAME);
+ $this->assertCount(5, $checkouts);
+ }
+
+ /**
+ * @dataProvider subtotalFilterDataProvider
+ *
+ * @param float $value
+ * @param string $filterType
+ * @param array $expectedCheckouts
+ */
+ public function testSubtotalFilter($value, $filterType, array $expectedCheckouts): void
+ {
+ $checkouts = $this->getDatagridData(
+ self::GRID_NAME,
+ [
+ sprintf('[%s][value]', 'subtotal') => $value,
+ sprintf('[%s][type]', 'subtotal') => $filterType
+ ]
+ );
+
+ $this->assertCount(count($expectedCheckouts), $checkouts);
+
+ $expectedCheckouts = $this->getCheckoutsByReferences($expectedCheckouts);
+ $actualCheckouts = $this->prepareCheckouts($checkouts);
+ $container = $this->getContainer();
+ /** @var Checkout $expectedCheckout */
+ foreach ($expectedCheckouts as $id => $expectedCheckout) {
+ $this->assertTrue(isset($actualCheckouts[$id]));
+ /** @var Subtotal $subtotal */
+ $subtotal = $expectedCheckout->getSubtotals()->first()->getSubtotal();
+
+ $formattedPrice = $container->get('oro_locale.formatter.number')->formatCurrency(
+ $subtotal->getAmount(),
+ $subtotal->getCurrency()
+ );
+
+ $actualCheckout = $actualCheckouts[$id];
+ $this->assertEquals($formattedPrice . "\n", $actualCheckout['subtotal']);
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function subtotalFilterDataProvider(): array
+ {
+ return [
+ 'greater than' => [
+ 'value' => self::SUBTOTAL_VALUE,
+ 'filterType' => NumberFilterTypeInterface::TYPE_GREATER_THAN,
+ 'expectedCheckouts' => ['checkout.1', 'alternative.checkout.2']
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider totalFilterDataProvider
+ *
+ * @param float $value
+ * @param string $filterType
+ * @param array $expectedCheckouts
+ */
+ public function testTotalFilter($value, $filterType, array $expectedCheckouts): void
+ {
+ $checkouts = $this->getDatagridData(
+ self::GRID_NAME,
+ [
+ sprintf('[%s][value]', 'total') => $value,
+ sprintf('[%s][type]', 'total') => $filterType
+ ]
+ );
+
+ $this->assertCount(count($expectedCheckouts), $checkouts);
+
+ $expectedCheckoutIds = array_keys($this->getCheckoutsByReferences($expectedCheckouts));
+ $actualCheckouts = $this->prepareCheckouts($checkouts);
+ /** @var Checkout $expectedCheckout */
+ foreach ($expectedCheckoutIds as $id) {
+ $this->assertTrue(isset($actualCheckouts[$id]));
+ $actualCheckout = $actualCheckouts[$id];
+ $this->assertEquals(
+ (float) $actualCheckout['subtotal'] + (float) $actualCheckout['shippingCost'],
+ (float) $actualCheckout['total']
+ );
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function totalFilterDataProvider(): array
+ {
+ return [
+ 'equal' => [
+ 'value' => self::TOTAL_VALUE,
+ 'filterType' => NumberFilterTypeInterface::TYPE_EQUAL,
+ 'expectedCheckouts' => ['checkout.1']
+ ]
+ ];
+ }
+
+ /**
+ * @param array|Checkout[] $checkoutReferences
+ * @return array
+ */
+ protected function getCheckoutsByReferences(array $checkoutReferences): array
+ {
+ $result = [];
+ foreach ($checkoutReferences as $checkoutReference) {
+ /** @var Checkout $checkout */
+ $checkout = $this->getReference($checkoutReference);
+ $result[$checkout->getId()] = $checkout;
+ }
+
+ return $result;
+ }
+
+ public function testSorters(): void
+ {
+ //check checkouts with subtotal sorter
+ $checkouts = $this->getDatagridData(
+ self::GRID_NAME,
+ [],
+ [
+ '[subtotal]' => OrmSorterExtension::DIRECTION_ASC,
+ ]
+ );
+ $this->checkSorting($checkouts, 'subtotal', OrmSorterExtension::DIRECTION_ASC);
+ }
+
+ /**
+ * @param array $checkouts
+ * @param string $column
+ * @param string $order
+ * @param bool $stringSorting
+ */
+ protected function checkSorting(array $checkouts, $column, $order, $stringSorting = false): void
+ {
+ $lastValue = null;
+ foreach ($checkouts as $checkout) {
+ /** @var Subtotal|string $actualValue */
+ $actualValue = $stringSorting ? $checkout[$column] : $this->getSubtotalValue($checkout['id']);
+ $actualValue = ($actualValue instanceof Subtotal) ? $actualValue->getAmount() : $actualValue;
+
+ if (null !== $lastValue) {
+ if ($order === OrmSorterExtension::DIRECTION_DESC) {
+ $this->assertGreaterThanOrEqual($actualValue, $lastValue);
+ } elseif ($order === OrmSorterExtension::DIRECTION_ASC) {
+ $this->assertLessThanOrEqual($actualValue, $lastValue);
+ }
+ }
+ $lastValue = $actualValue;
+ }
+ }
+
+ /**
+ * @param string $gridName
+ * @param array $filters
+ * @param array $sorters
+ * @return array
+ */
+ protected function getDatagridData($gridName, array $filters = [], array $sorters = []): array
+ {
+ $result = [];
+ foreach ($filters as $filter => $value) {
+ $result[$gridName . '[_filter]' . $filter] = $value;
+ }
+ foreach ($sorters as $sorter => $value) {
+ $result[$gridName . '[_sort_by]' . $sorter] = $value;
+ }
+
+ $response = $this->client->requestFrontendGrid(['gridName' => $gridName], $result);
+
+ return json_decode($response->getContent(), true)['data'];
+ }
+
+ /**
+ * @param array $checkouts
+ * @return array
+ */
+ protected function prepareCheckouts(array $checkouts): array
+ {
+ $result = [];
+ foreach ($checkouts as $checkout) {
+ $result[$checkout['id']] = $checkout;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param integer $checkoutId
+ * @return float
+ */
+ protected function getSubtotalValue($checkoutId): float
+ {
+ $checkout = $this->getCheckoutById($checkoutId);
+ if (0 === $checkout->getSubtotals()->count()) {
+ return 0;
+ }
+
+ return $checkout->getSubtotals()->first()->getSubtotal()->getAmount();
+ }
+
+ /**
+ * @param int $checkoutId
+ * @return Checkout
+ */
+ protected function getCheckoutById($checkoutId): ?Checkout
+ {
+ if (empty($this->allCheckouts)) {
+ /** @var array|Checkout[] $checkouts */
+ $checkouts = $this->getContainer()->get('doctrine')
+ ->getManagerForClass('OroCheckoutBundle:Checkout')
+ ->getRepository('OroCheckoutBundle:Checkout')
+ ->findAll();
+
+ foreach ($checkouts as $checkout) {
+ $this->allCheckouts[$checkout->getId()] = $checkout;
+ }
+ }
+
+ return $this->allCheckouts[$checkoutId];
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/Controller/WorkflowDefinitionControllerTest.php b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/Controller/WorkflowDefinitionControllerTest.php
new file mode 100644
index 0000000..018b691
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/Controller/WorkflowDefinitionControllerTest.php
@@ -0,0 +1,25 @@
+loadFixtures([LoadTranslations::class]);
+ }
+
+ public function testCheckoutWorkflowViewPage(): void
+ {
+ $this->assertCheckoutWorkflowCorrectViewPage(
+ 'b2b_flow_alternative_checkout',
+ 'Alternative Checkout',
+ 'b2b_checkout_flow'
+ );
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/DataFixtures/LoadQuoteAlternativeCheckoutsData.php b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/DataFixtures/LoadQuoteAlternativeCheckoutsData.php
new file mode 100644
index 0000000..62e4a29
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/DataFixtures/LoadQuoteAlternativeCheckoutsData.php
@@ -0,0 +1,79 @@
+getPaymentMethodIdentifier($this->container);
+
+ return [
+ self::CHECKOUT_1 => [
+ 'customerUser' => LoadCustomerUserData::EMAIL,
+ 'source' => LoadQuoteProductDemandData::QUOTE_DEMAND_1,
+ 'checkout' => ['payment_method' => $paymentMethodIdentifier],
+ ],
+ self::CHECKOUT_2 => [
+ 'source' => LoadQuoteProductDemandData::QUOTE_DEMAND_2,
+ 'checkout' => ['payment_method' => $paymentMethodIdentifier],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getWorkflowName(): string
+ {
+ return 'b2b_flow_alternative_checkout';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createCheckout(): Checkout
+ {
+ return new Checkout();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getCheckoutSourceName(): string
+ {
+ return 'quoteDemand';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDependencies(): array
+ {
+ return array_merge(
+ parent::getDependencies(),
+ [
+ LoadQuoteProductDemandData::class,
+ LoadPaymentTermData::class,
+ LoadPaymentMethodsConfigsRuleData::class,
+ ]
+ );
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/DataFixtures/LoadQuoteAlternativeCheckoutsSubtotalsData.php b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/DataFixtures/LoadQuoteAlternativeCheckoutsSubtotalsData.php
new file mode 100644
index 0000000..f55c5f0
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/DataFixtures/LoadQuoteAlternativeCheckoutsSubtotalsData.php
@@ -0,0 +1,52 @@
+ [
+ 'checkout' => LoadQuoteAlternativeCheckoutsData::CHECKOUT_1,
+ 'currency' => 'USD',
+ 'amount' => 600,
+ 'valid' => true,
+ ],
+ self::ALTERNATIVE_CHECKOUT_SUBTOTAL_2 => [
+ 'checkout' => LoadQuoteAlternativeCheckoutsData::CHECKOUT_2,
+ 'currency' => 'USD',
+ 'amount' => 700,
+ 'valid' => true,
+ ],
+ ]
+ );
+
+ parent::load($manager);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDependencies(): array
+ {
+ return array_merge(
+ parent::getDependencies(),
+ [
+ LoadQuoteAlternativeCheckoutsData::class,
+ ]
+ );
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/DataFixtures/LoadTranslations.php b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/DataFixtures/LoadTranslations.php
new file mode 100644
index 0000000..37731b8
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Functional/DataFixtures/LoadTranslations.php
@@ -0,0 +1,17 @@
+initClient();
+ $this->loadFixtures(
+ [
+ LoadQuoteAlternativeCheckoutsData::class,
+ BaseLoadQuoteCheckoutsData::class,
+ LoadCustomerUserData::class,
+ ]
+ );
+ }
+
+ /**
+ * @return CheckoutRepository
+ */
+ protected function getRepository(): CheckoutRepository
+ {
+ return $this->getContainer()->get('doctrine')->getRepository(Checkout::class);
+ }
+
+ /**
+ * @param string $checkout
+ * @param string $workflowName
+ * @dataProvider findCheckoutByCustomerUserAndSourceCriteriaByQuoteDemandProvider
+ */
+ public function testFindCheckoutByCustomerUserAndSourceCriteriaByQuoteDemand($checkout, $workflowName): void
+ {
+ $customerUser = $this->getReference(LoadCustomerUserData::EMAIL);
+ $criteria = ['quoteDemand' => $this->getReference(LoadQuoteProductDemandData::QUOTE_DEMAND_1)];
+
+ $this->assertSame(
+ $this->getReference($checkout),
+ $this->getRepository()->findCheckoutByCustomerUserAndSourceCriteriaWithCurrency(
+ $customerUser,
+ $criteria,
+ $workflowName
+ )
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function findCheckoutByCustomerUserAndSourceCriteriaByQuoteDemandProvider(): array
+ {
+ return [
+ 'checkout' => [
+ BaseLoadQuoteCheckoutsData::CHECKOUT_1,
+ 'b2b_flow_checkout',
+ ],
+ 'alternative checkout' => [
+ LoadQuoteAlternativeCheckoutsData::CHECKOUT_1,
+ 'b2b_flow_alternative_checkout',
+ ],
+ ];
+ }
+}
diff --git a/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Unit/EventListener/QuantityToOrderConditionListenerTest.php b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Unit/EventListener/QuantityToOrderConditionListenerTest.php
new file mode 100644
index 0000000..882e998
--- /dev/null
+++ b/src/Oro/Bundle/AlternativeCheckoutBundle/Tests/Unit/EventListener/QuantityToOrderConditionListenerTest.php
@@ -0,0 +1,23 @@
+quantityToOrderConditionListener = new QuantityToOrderConditionListener(
+ $this->validatorService,
+ $this->doctrineHelper
+ );
+ }
+}