Skip to content

Instantly share code, notes, and snippets.

@WengerK
Last active June 3, 2022 09:14

Revisions

  1. WengerK revised this gist Jun 3, 2022. 3 changed files with 228 additions and 0 deletions.
    113 changes: 113 additions & 0 deletions OrderTotalPriceRange.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,113 @@
    <?php

    namespace Drupal\my_custom_commerce_range_condition\Plugin\Commerce\Condition;

    use Drupal\commerce\Plugin\Commerce\Condition\ConditionBase;
    use Drupal\commerce_price\Price;
    use Drupal\Core\Entity\EntityInterface;
    use Drupal\Core\Form\FormStateInterface;

    /**
    * Provides the total price range condition for orders.
    *
    * To be used when willing to trigger condition when the order total falls
    * within a range of price (including adjustments)
    * e.g. $100 => order total <= $200.
    *
    * @CommerceCondition(
    * id="order_total_price_range",
    * label=@Translation("Total price range"),
    * display_label=@Translation("Current order total within a range"),
    * category=@Translation("Order", context="Commerce"),
    * entity_type="commerce_order",
    * )
    */
    class OrderTotalPriceRange extends ConditionBase {

    /**
    * {@inheritdoc}
    */
    public function defaultConfiguration() {
    return [
    'amount_from' => NULL,
    'amount_to' => NULL,
    ] + parent::defaultConfiguration();
    }

    /**
    * {@inheritdoc}
    */
    public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildConfigurationForm($form, $form_state);

    $amount_from = $this->configuration['amount_from'];
    $amount_to = $this->configuration['amount_to'];

    // An #ajax bug can cause $amount_from to be incomplete.
    if (isset($amount_from) && !isset($amount_from['number'], $amount_from['currency_code'])) {
    $amount_from = NULL;
    }

    // An #ajax bug can cause $amount_to to be incomplete.
    if (isset($amount_to) && !isset($amount_to['number'], $amount_to['currency_code'])) {
    $amount_to = NULL;
    }

    $form['amount_from'] = [
    '#type' => 'commerce_price',
    '#title' => $this->t('Amount from'),
    '#default_value' => $amount_from,
    '#required' => TRUE,
    ];

    $form['amount_to'] = [
    '#type' => 'commerce_price',
    '#title' => $this->t('Amount to'),
    '#default_value' => $amount_to,
    '#required' => TRUE,
    ];

    return $form;
    }

    /**
    * {@inheritdoc}
    */
    public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    parent::submitConfigurationForm($form, $form_state);

    $values = $form_state->getValue($form['#parents']);
    $this->configuration['amount_from'] = $values['amount_from'];
    $this->configuration['amount_to'] = $values['amount_to'];
    }

    /**
    * {@inheritdoc}
    */
    public function evaluate(EntityInterface $entity) {
    $this->assertEntity($entity);
    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
    $order = $entity;
    $total_price = $order->getTotalPrice();

    if (!$total_price) {
    return FALSE;
    }

    $condition_price_from = Price::fromArray($this->configuration['amount_from']);

    if ($total_price->getCurrencyCode() !== $condition_price_from->getCurrencyCode()) {
    return FALSE;
    }

    $condition_price_to = Price::fromArray($this->configuration['amount_to']);

    if ($total_price->getCurrencyCode() !== $condition_price_to->getCurrencyCode()) {
    return FALSE;
    }

    return $total_price->greaterThanOrEqual($condition_price_from)
    && $total_price->lessThanOrEqual($condition_price_to);
    }

    }
    106 changes: 106 additions & 0 deletions OrderTotalPriceRangeTest.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,106 @@
    <?php

    namespace Drupal\Tests\my_custom_commerce_range_condition\Unit\Plugin\Commerce\Condition;

    use Drupal\commerce_order\Entity\OrderInterface;
    use Drupal\commerce_price\Price;
    use Drupal\my_custom_commerce_range_condition\Plugin\Commerce\Condition\OrderTotalPriceRange;
    use Drupal\Tests\UnitTestCase;

    /**
    * @coversDefaultClass \Drupal\my_custom_commerce_range_condition\Plugin\Commerce\Condition\OrderTotalPriceRange
    * @group commerce
    *
    * @internal
    */
    final class OrderTotalPriceRangeRangeTest extends UnitTestCase {

    /**
    * ::covers evaluate.
    */
    public function testEmptyOrder() {
    $condition = new OrderTotalPriceRange([
    'amount_from' => [
    'number' => '10.00',
    'currency_code' => 'EUR',
    ],
    'amount_to' => [
    'number' => '25.00',
    'currency_code' => 'EUR',
    ],
    ], 'order_total_price', ['entity_type' => 'commerce_order']);
    $order = $this->prophesize(OrderInterface::class);
    $order->getEntityTypeId()->willReturn('commerce_order');
    $order->getTotalPrice()->willReturn(NULL);
    $order = $order->reveal();

    self::assertFalse($condition->evaluate($order));
    }

    /**
    * ::covers evaluate.
    */
    public function testMismatchedCurrencies() {
    $condition = new OrderTotalPriceRange([
    'amount_from' => [
    'number' => '10.00',
    'currency_code' => 'EUR',
    ],
    'amount_to' => [
    'number' => '25.00',
    'currency_code' => 'EUR',
    ],
    ], 'order_total_price', ['entity_type' => 'commerce_order']);
    $order = $this->prophesize(OrderInterface::class);
    $order->getEntityTypeId()->willReturn('commerce_order');
    $order->getTotalPrice()->willReturn(new Price('10.00', 'USD'));
    $order = $order->reveal();

    self::assertFalse($condition->evaluate($order));
    }

    /**
    * ::covers evaluate.
    *
    * @dataProvider totalPriceProvider
    */
    public function testEvaluate($amount_from, $amount_to, $given_total_price, $result) {
    $condition = new OrderTotalPriceRange([
    'amount_from' => [
    'number' => $amount_from,
    'currency_code' => 'USD',
    ],
    'amount_to' => [
    'number' => $amount_to,
    'currency_code' => 'USD',
    ],
    ], 'order_total_price', ['entity_type' => 'commerce_order']);
    $order = $this->prophesize(OrderInterface::class);
    $order->getEntityTypeId()->willReturn('commerce_order');
    $order->getTotalPrice()->willReturn(new Price($given_total_price, 'USD'));
    $order = $order->reveal();

    self::assertEquals($result, $condition->evaluate($order));
    }

    /**
    * Data provider for ::testEvaluate.
    *
    * @return array
    * A list of testEvaluate function arguments.
    */
    public function totalPriceProvider() {
    return [
    [10, 25, 5, FALSE],
    [10, 25, 9, FALSE],
    [10, 25, 10, TRUE],
    [10, 25, 11, TRUE],
    [10, 25, 20, TRUE],
    [10, 25, 24, TRUE],
    [10, 25, 25, TRUE],
    [10, 25, 26, FALSE],
    [10, 25, 100, FALSE],
    ];
    }

    }
    9 changes: 9 additions & 0 deletions my_custom_commerce_range_condition.info.yml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    name: My Custom Commerce Range Condition
    type: module
    description: Module used to handle Commerce Range Condition
    core_version_requirement: ^9.1
    package: Commerce
    dependencies:
    - commerce:commerce
    - commerce:commerce_order
    - commerce:commerce_product
  2. WengerK created this gist Jun 3, 2022.
    7 changes: 7 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    # Article Ressources - How to setup a Drupal Commerce Order total range condition

    This is the Gist repository for my article **Drupal Commerce Order total range condition**.

    Be aware that this article has been wrote for the Blog of [Antistatique](https://antistatique.net) — Web Agency in Lausanne, Switzerland. A place where I work as Full Stack Web Developer.

    Feel free to read it the full article on [Medium](https://medium.com/@WengerK) or check it out on [Antistatique](https://antistatique.net).