- 🇳🇱Netherlands ndf Amsterdam
Problem/Motivation
When a product is added to the cart (i.e., a brand-new order is created in "draft" state), there are no billing or shipping profiles
available on the order. Drupal Commerce’s Order::collectProfiles() method thus returns an empty array. The AvaTax module attempts to resolve a shipping/billing profile, but encounters a null (empty) profile and subsequently triggers an error:TypeError: Drupal\commerce_tax\Event\CustomerProfileEvent::__construct(): Argument #1 ($order_item) must be of type Drupal\commerce_order\Entity\OrderItemInterface, null given, called in /var/www/html/docroot/modules/contrib/commerce_avatax/src/AvataxLib.php on line 509 in Drupal\commerce_tax\Event\CustomerProfileEvent->__construct() (line 38 of /var/www/html/docroot/modules/contrib/commerce/modules/tax/src/Event/CustomerProfileEvent.php).
This issue arises specifically during the add-to-cart action, prior to any checkout steps that collect billing or shipping addresses.
Steps to reproduce
1 Install and enable Drupal Commerce 2.x and commerce_avatax.
2 Configure AvaTax settings.
3 Add a product to the cart (or create a new order with no shipping/billing information).
4 Observe the error, as no profiles exist on the brand-new draft order at that stage.Proposed resolution
• Update AvaTax’s AvataxLib::prepareTransactionsCreate() logic to gracefully handle cases where a shipping or billing profile is not yet present on the order item. This prevents a null reference from being passed to CustomerProfileEvent.
• Alternatively, add checks in resolveCustomerProfile() to ensure that an empty profile is skipped if it doesn’t exist.Remaining tasks:
• [ ] Discuss how AvaTax expects to handle orders at an early "add to cart" stage.
• [ ] Decide whether focus should be on gracefully handling missing profiles or preventing AvaTax from being applied to incomplete draft orders.
• [ ] Provide patch or merge request in the commerce_avatax repository.
• [ ] Write tests confirming that the order can still be placed into the cart with no fail when no billing/shipping profile is present.API changes:
None anticipated. The solution only adds safety checks around order item profiles.
Data model changes:
None anticipated.
Additional information:
• This behavior is consistent with Drupal Commerce 2.x, as new orders initially have no stored address profile.
• The error occurs only when the AvaTax integration tries to calculate tax for a brand-new order with no addresses. - 🇳🇱Netherlands ndf Amsterdam
This is actually fixed in the DEV version in this commit:
https://git.drupalcode.org/project/commerce_avatax/-/commit/d79662a0685b...
try { // Allow the customer profile to be altered, per order item. $event = new CustomerProfileEvent($customer_profile, $order_item); } catch (\Throwable $t) { // The order of parameters changed in Commerce 3, this is our way of // supporting both versions. $event = new CustomerProfileEvent($order_item, $customer_profile); }
Now knowing this, I think we should either set commerce:^2.0 as requirement on the stable version of commerce_avatax. Or up the stable version to include the support of both commerce core version in the stable branch of commerce_avatax.
Without it one cannot add items to the cart with commerce_avatax stable + commerce_core 2.0
- 🇳🇱Netherlands ndf Amsterdam
Instead of try/catch, named parameters would be a better IMO. With the order of Commerce 3.x
$event = new CustomerProfileEvent(order_item: $order_item, customer_profile: $customer_profile);
PHP 8 is required for this, which is already declared in composer.json.
3.x constructor of Drupal\commerce_tax\Event\CustomerProfileEvent.php
/** * Constructs a new CustomerProfileEvent. * * @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item * The order item. * @param \Drupal\profile\Entity\ProfileInterface|null $customer_profile * The initially selected customer profile, NULL if not yet known. */ public function __construct(OrderItemInterface $order_item, ?ProfileInterface $customer_profile = NULL) { $this->orderItem = $order_item; $this->customerProfile = $customer_profile; }
2.4 constructor of Drupal\commerce_tax\Event\CustomerProfileEvent.php
/** * Constructs a new CustomerProfileEvent. * * @param \Drupal\profile\Entity\ProfileInterface $customer_profile * The initially selected customer profile. * @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item * The order item. */ public function __construct(ProfileInterface $customer_profile = NULL, OrderItemInterface $order_item) { $this->customerProfile = $customer_profile; $this->orderItem = $order_item; }