Promotions require order_types but never indicate that

Created on 23 July 2024, 3 months ago

If a site has only one order type created, the Promotions UI will not show the "Order type" checkbox. However, order type is still silently required when creating a Promotion programmatically, and set order types will not be shown in the JSON:API. This caused us a day of discovery before we could nail down why our programmatically-created Promotions were not applying.

Our original pseudo-code looked like the following.

    $basePromotion = Promotion::create([
      'uid' => \Drupal::currentUser()->id(),
      'name' => $basePromotionName,
      'require_coupon' => TRUE,
      'compatibility' => 'any',
      'offer' => [
        'target_plugin_id' => 'order_item_percentage_off',
        'target_plugin_configuration' => [
          'percentage' => '1',
          'display_inclusive' => FALSE,
          'operator' => 'OR',
          'conditions' => [
            'target_plugin_configuration' => [
              'plugin' => 'order_item_purchased_entity:commerce_product_variation',
              'configuration' => [
                'entities' => [
                  $productVariation->id() => $productVariation->uuid(),
                ],
              ],
            ],
          ],
        ],
      ],
    ]);
    $basePromotion->save();

    Coupon::create([
      'promotion_id' => $basePromotion->id(),
      'code' => $couponCode,
      'usage_limit' => 1,
      'usage_limit_customer' => 1,
    ])->save();

The above was based on the results we saw in a JSON:API export after having created a sample Promotion through the UI (endpoint: commerce_promotion/commerce_promotion). In those JSON:API exports, there IS a relationships object that mentions order_types, but the data array is NEVER filled. Even when the Promotion is working fine (below), order_types data remains empty. That's what I'm calling a "bug" here.

With the above code, if you try to now redeem $couponCode, it won't work - saying the code is either invalid or expired or already redeemed. You can immediately "fix" this, and see the "silent"ness of order_types by going into the Promotions UI and simply re-saving the Promotion. No need to change anything - just resave it. Doing so silently "fixes" the missing (from the API call above) order_types, and the coupon code starts to work.

At this point, if you went back to the JSON:API export (endpoint: commerce_promotion/commerce_promotion), the relationships order_types is still empty. I would have assumed, here, that we would see the order_types being applied (which, in turn, would have told us "you need this when writing API stuff!").

To "fix" this from a ::create standpoint, you just need to add:

    $basePromotion = Promotion::create([
    ...
      'order_types' => [
        ['target_id' => 'ORDER_TYPE_ID],
      ],

The code in question that requires order_types is in promotion/src/Entity/ Promotion.php::available:

    if (!in_array($order->bundle(), $this->getOrderTypeIds())) {
      return FALSE;
    }

I don't currently know why the order_type is not showing in a JSON:API request.

πŸ› Bug report
Status

Active

Version

2.0

Component

Developer experience

Created by

πŸ‡ΊπŸ‡ΈUnited States morbus iff

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Production build 0.71.5 2024