After latest dev branch update to commerce on D8, started having a crash when adding a new order manually. Did some debugging, here is the error:

TypeError: Argument 1 passed to Drupal\\commerce_product\\Plugin\\Field\\FieldWidget\\ProductVariationWidgetBase::loadEnabledVariations() must be an instance of Drupal\\commerce_product\\Entity\\ProductInterface, null given, called in web/modules/contrib/commerce/modules/product/src/Plugin/Field/FieldWidget/ProductVariationAttributesWidget.php on line 96 in web/modules/contrib/commerce/modules/product/src/Plugin/Field/FieldWidget/ProductVariationWidgetBase.php on line 167 #0 web/modules/contrib/commerce/modules/product/src/Plugin/Field/FieldWidget/ProductVariationAttributesWidget.php(96): Drupal\\commerce_product\\Plugin\\Field\\FieldWidget\\ProductVariationWidgetBase->loadEnabledVariations(NULL)

Managed to temporarily fix it by adding this line to commerce/modules/product/src/plugin/field/fieldwidget/productvariationattributeswidget.php at line 95:

    $product = $form_state->get('product');
    
    /* Custom to fix error, check if product is null */
    if (!$product) {
      $form_state->set('hide_form', TRUE);
      $element['variation'] = [
        '#type' => 'value',
        '#value' => 0,
      ];
      return $element;
    }
    /* End Custom to fix error */


    $variations = $this->loadEnabledVariations($product);

I believe this is happening because when I manually create an order through the orders view, there is no product set yet, and that causes $this->loadEnabledVariations($product) to throw an error, because $product is null. I used a little work around as I don't know best practices for checking to see if $product is null (maybe it should be done in loadEnabledVariations(), and just have it return 0 if the product is null). Just throwing this here in case anyone else comes across it.

Comments

Gode.Agarunov created an issue. See original summary.

bojanz’s picture

Why would ProductVariationAttributesWidget run on the order admin UI?
Did you change the order item purchased_entity widget yourself?

All of our tests are green, but I don't think we have test coverage for using an add to cart widget on the order admin screen.

Gode.Agarunov’s picture

Just wanted to add that I'm still having an error trying to create an order manually, the order itself creates now with above fix but I still can't add any products, as I get an AJAX error. Here is the debug info on that:

Error: Call to a member function getDefaultVariation() on null in web/modules/contrib/commerce/modules/product/src/Plugin/Field/FieldWidget/ProductVariationAttributesWidget.php on line 215 #0

Which is this function:

  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
    /** @var \Drupal\commerce_product\Entity\ProductInterface $product */
    $product = $form_state->get('product');
    $default_variation = $product->getDefaultVariation();
    $variations = $this->variationStorage->loadEnabled($product);

    foreach ($values as &$value) {
      $attribute_values = isset($value['attributes']) ? $value['attributes'] : [];
      $selected_variation = $this->variationAttributeMapper->selectVariation($variations, $attribute_values);
      if ($selected_variation) {
        $value['variation'] = $selected_variation->id();
      }
      else {
        $value['variation'] = $default_variation->id();
      }
    }

    return parent::massageFormValues($values, $form, $form_state);
  }

I'm not exactly sure how to handle this one, as it returns to the parent function it is a child of, so I probably can't just do a return null if the product is null.

Any ideas?

Gode.Agarunov’s picture

@bojanz

The website is still in development, and I was asked to create a way for orders in commerce->orders to show up with a flag if they have not been viewed yet. In order to begin working on this, I went to add a new order manually through the admin UI (commerce->orders->create a new order), and that is when I experienced this crash. I do not believe that I have any custom code related to this in any way, my commerce module is otherwise stock, as I don't change anything at the contrib module level unless I absolutely have to, and even then I only do it temporarily.

I'm not sure why it is running, but I'm assuming that what is happening is I create a new order and it goes to load products.

However, this may be related to my own custom module, which emails the receipt of a new order to the sales user selected by the purchaser during registration, perhaps this is what is triggering this code to be run? It uses OrderCompleteSubscriber

<?php

namespace Drupal\sahara_wireless\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\state_machine\Event\WorkflowTransitionEvent;
use Drupal\commerce\MailHandlerInterface;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_order\OrderTotalSummaryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
 * Class OrderCompleteSubscriber.
 *
 * @package Drupal\sahara_wireless
 */
class OrderCompleteSubscriber implements EventSubscriberInterface {

  /**
   * Drupal\Core\Entity\EntityTypeManager definition.
   *
   * @var \Drupal\Core\Entity\EntityTypeManager
   */
  protected $entityTypeManager;

  /**
   * The profile view builder.
   *
   * @var \Drupal\profile\ProfileViewBuilder
   */
  protected $profileViewBuilder;

  /**
   * The mail handler.
   *
   * @var \Drupal\commerce\MailHandlerInterface
   */
  protected $mailHandler;

  /**
   * The order total summary.
   *
   * @var \Drupal\commerce_order\OrderTotalSummaryInterface
   */
  protected $orderTotalSummary;

  /**
   * Constructor.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, MailHandlerInterface $mail_handler, OrderTotalSummaryInterface $order_total_summary) {
    $this->entityTypeManager = $entity_type_manager;
    $this->mailHandler = $mail_handler;
    $this->orderTotalSummary = $order_total_summary;
    $this->profileViewBuilder = $entity_type_manager->getViewBuilder('profile');
  }

  /**
   * {@inheritdoc}
   */
  static function getSubscribedEvents() {
    $events['commerce_order.place.post_transition'] = ['orderCompleteHandler'];

    return $events;
  }

  /**
   * This method is called whenever the commerce_order.place.post_transition event is
   * dispatched.
   *
   * @param WorkflowTransitionEvent $event
   */
  public function orderCompleteHandler(WorkflowTransitionEvent $event) {
    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
    $order = $event->getEntity();
    $items = $order->getItems();

    if ($order) {
      $customer = $order->getCustomer();
      if (!$customer->isAnonymous()) {
        $list = \Drupal::entityTypeManager()
          ->getStorage('profile')
          ->loadByProperties([
            'uid' => $customer->id(),
            'type' => 'wholesaler',
          ]);
        $profile = current($list);
        if ($profile) {
          if ($profile->hasField('field_sales_associate')) {
            $salesAss = $profile->get('field_sales_associate')->first()->getValue();
            $salesAcc = \Drupal\user\Entity\User::load($salesAss['target_id']);
            if ($salesAcc) {
              $salesEmail = $salesAcc->getEmail();
              if ($salesEmail) {
                $mailManager = \Drupal::service('plugin.manager.mail');
                $module = '***********';
                $key = 'wholesale_order_new';


                $to = $salesEmail;
                if (!$to) {
                  return false;
                }

                $bcc = "*******************";

                $subject = 'New Wholesale Order #' . $order->getOrderNumber() . ' Completed';
                $body = [
                  '#theme' => 'commerce_order_receipt',
                  '#order_entity' => $order,
                  '#totals' => $this->orderTotalSummary->buildTotals($order),
                ];

                if ($billing_profile = $order->getBillingProfile()) {
                  $body['#billing_information'] = $this->profileViewBuilder->view($billing_profile);
                }

                $params = [
                  'id' => 'order_receipt',
                  'from' => $order->getStore()->getEmail(),
                  'bcc' => $bcc,
                  'order' => $order,
                ];

                return $this->mailHandler->sendMail($to, $subject, $body, $params);
              }
            }
          }
        }
      }

    }
  }

}

Perhaps this is what is responsible?

Edit:

Actually looking over it, this is probably exactly what is responsible, probably $order->getItems() that's kicking all of this off. I will deal with it, thanks.

bojanz’s picture

Can you give us an update here? Were you able to resolve the problem, and what was it?

Gode.Agarunov’s picture

@bojanz

Oh yeah, got it working, it was because of the order item purchased entity form display mode was set to "product variation attributes", ended up googling around even more and found a post on IIRC stack overflow describing a similar issue and fix, and once I changed it to autocomplete I had no more issues. Hopefully this helps anyone else who comes across it.

bojanz’s picture

Status: Active » Closed (works as designed)

Thanks for the update!

idiaz.roncero’s picture

Just FYI, I just had the same issue and the solution on #6 saved my live.

thetwentyseven’s picture

I am just going to put the path because it took me sometime to understand #6

Go to Administration > Commerce > Configuration > Order item types > Edit Default > Manage form display

or /admin/commerce/config/order-item-types/default/edit/form-display

and change on Purchased entity the widget from "Product variation attributes" to "Autocomplete"

dbroll’s picture

I also ran into this, but because I changed the form-display to "Product variation attributes" so that the user could select between the variations. On my install I don't have an add-to-cart and default displays available on this page like what is visible in the documentation.

[edit]

Not sure if this is normally installed with one of the modules but I added the display mode manually by going to Structure > Display modes > Form modes > Add form mode (Order item -> named "Add to cart")

Now I can have separate views for Admin -> orders and the actual add to cart forms that the purchaser sees.

Problue Solutions’s picture

@dbroll - how do you use the default form display for the admin UI instead of the add to cart form display? I have both, default is set to autocomplete and add to cart is set to product variation attributes, so it seems when adding an order from admin UI its using the add to cart form display instead of default, how did you force the default one to be used?