Problem/Motivation

If a donation item is not present on the order, then DonationItemPaneBase chokes. I don't know whether or not this would be an issue in the provided donation checkout flow, but it's likely that you could encounter a situation where the donation pane is present on a custom checkout flow with an optional donation for an order without a donation item.

This may be related to https://www.drupal.org/project/commerce_donation_flow/issues/3239498. Don't know whether you want to roll these together or not. I think the issue of operating on a null/missing donationItem could be fairly omnipresent given the initial intended use case of the module.

Proposed resolution

Attaching a very simple patch that just returns an empty array if a donation item isn't present, which essentially just results in no markup being rendered for the pane.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mrweiner created an issue. See original summary.

mrweiner’s picture

Status: Active » Needs review
StatusFileSize
new1.58 KB

EDIT: Whoops, may not be applying

mrweiner’s picture

This one doesn't reorder the use statements.

mrweiner’s picture

Status: Needs review » Needs work

Not my day, ha. This is an error on bool not on null. Guess I forgot how to read.

mrweiner’s picture

Status: Needs work » Needs review
StatusFileSize
new731 bytes

Alright, just a simple ! check instead of isset

fathershawn’s picture

fathershawn’s picture

Assigned: Unassigned » fathershawn
Status: Needs review » Needs work

mrweiner’s picture

@FatherSean I think this updated approach makes sense. I just made some small changes to simplify the logic in initDonationItem(). What do you think? Also, do you think this bit is needed anymore?

// Ensure an order item is set and easily found.
$items = $instance->order->getItems();
if (empty($items)) {
  /* @see \Drupal\commerce_donation_flow\NewOrder::get() */
  throw new \UnexpectedValueException('Order Items not properly initialized');
}

If the there are no items on the order then I think the while loop will handle that on its own by creating one.

mrweiner’s picture

And actually a question about submitPaneForm().

  /**
   * {@inheritdoc}
   */
  public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) {
    $donationItem = $form_state->get('donation_item');
    if ($donationItem instanceof OrderItemInterface) {
      $donationItem->save();
    }
    else {
      // Allow extending panes to catch an Exception if desired to deal with
      // a missing donation OrderItem.
      throw new MissingDonationItemException('Instance of OrderItemInterface not stored as `donation_item` in FormState');
    }
  }

I don't know if we necessarily want to throw an error. If the orderItem was deleted/unset before this method fires, wouldn't it be valid to just not save the item? The exception might problematic because I don't think it could be caught by any logic other than an extending pane. If somebody is using the default pane, they don't really have a way to catch the exception. Actually, if this is a problem here, then the LengthException I added in validatePaneForm could be problematic as well.

fathershawn’s picture

Status: Needs work » Needs review

@mrweiner your feedback over all and most recently in #9 prompted me to take another step back and refactor some more. This latest iteration removes storing the donation item and allows these panes, I hope, to live in any checkout flow.

As to #10, that submission method is in the base plugin class, which cannot be a pane on it's own. Since the purpose of these panes is to edit the donation order item, it seems valid to me to normally expect the order item to persist to the submit method so it can be saved. If an extending pane is removing it on purpose, this allows that extending pane to handle the issue.

fathershawn’s picture

If this works for your use case, I'll add another pane that simply provides a configurable checkbox element, which when checked will add a donation order item to the order on submission. When placed in an earlier step than our donation panes, such a pane would essentially enable our panes as optional panes if a donation was added.

mrweiner’s picture

Oh right, I forgot that this was a Base class so yes, those exceptions do make sense. I'll need to do some testing to see how the latest changes work with our flow, but they all look like they should work out fine.

One thing I notice is that a null $donationItem could be passed into buildDonationItem() and I'm not sure if there's handling for that. Might need to add a guard to buildDonationItem() to ensure we aren't trying to call methods on a NULL item.

Re #12, not sure I'm totally understanding what you mean. That said, at the moment we are using the default checkout flow with the donation pane on the "Order Information" step, which is the first pane that a customer encounters unless they are anonymous and hit the Login step. In the case of a logged in user, I don't know that they would have the chance to encounter this proposed new pane before hitting the step with the donation pane. I'm happy to test things out, though.

mrweiner’s picture

Oh just one other thought. It's certainly preference so could go either way, but I wonder if getDonationItem() might be a better name than donationItem() now that it's acting as a getter method instead of a property?

mrweiner’s picture

Oh for #12, if the idea is to make the donation optional, we are actually handling that on our own by adding a checkbox to the donation pane itself to keep or remove a donation and apply the user's desired pricing. When the order is created, we are using NewOrder::addDonationItem() to create a $0 donation item and just hiding it in the order summary. Then when they submit that pane and continue to review the order, the donation item price is updated or the item removed based on their selections.

It would probably be more graceful to not need to add the $0 donation item on order creation. I don't think this was possible with the current alpha/dev, but might be possible with all of these updates.

fathershawn’s picture

Status: Needs review » Fixed

I believe this is fixed in 1.1.x

fathershawn’s picture

Status: Fixed » Closed (fixed)
fathershawn’s picture

Version: 1.0.x-dev » 1.1.x-dev
Status: Closed (fixed) » Needs work

fathershawn’s picture

Status: Needs work » Needs review
StatusFileSize
new17.38 KB

Moved this work to 1.1.x, added a patch to capture the MR for use in composer.json

fathershawn’s picture

Status: Needs review » Reviewed & tested by the community

All tests passing - ready to merge

  • FatherShawn committed 0c321aa6 on 1.1.x
    Issue #3243886 by FatherShawn, mrweiner: Alter method of getting the...
fathershawn’s picture

Status: Reviewed & tested by the community » Fixed
fathershawn’s picture

Title: Error: Call to a member function getEntityTypeId() on bool in Drupal\commerce_donation_flow\Plugin\Commerce\CheckoutPane\DonationItemPaneBase->doSummaryBuild() (line 191 of modules/contrib/commerce_donation_flow/src/Plugin/Commerce/CheckoutPane/DonationIt » Alter method of getting the donation item

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.