I had a talk with Ryan on IRC yesterday about a few issues concerning shipping and payment modules. One of the issues that arose, is that the cart module should reset the order status to cart, if the contents of the shopping cart is changed.

• If the order status is 'cart' there isn't any need to change the order status.

• If the order status is checkout_review, the order status should be changed to cart.

• If the order status is checkout_payment, I'm a bit unsure what should happen. It should be impossible for users as is to add things to basked when they are on payment step. Right now though, anonymous users can do this, due to a bug #1001526: Anonymous users can put things into cart when they are at the payment step.. I do think, however, that it would make most sense to change the order status if a user were able to change the contents of the cart during payment step. It doesn't make sense to be able to change the contents of the cart and keep the status at payment.

Comments

googletorp’s picture

Status: Active » Needs review

I created a patch for this at github.

One thing though, in order for this patch to work, the patch from #1001288: Inconsistant implementation of save method on DrupalDefaultEntityController subclasses. is needed, as hook_entityType_presave is needed to detect the change which currently isn't invoked created entities.

rszrama’s picture

Status: Needs review » Needs work

Since this depends on the other issue and I put it back to needs work, we'll need to wait for its inclusion before putting this one in. One observation, though, is that instead of hard coding an array of $changeable_order_types (which is a misnomer anyways, since it's order statuses), we should instead use the cart property of order statuses. This will make it consistent with the cart module:

  // Create an array of valid shopping cart order statuses.
  $status_ids = array_keys(commerce_order_statuses(array('cart' => TRUE)));
googletorp’s picture

Status: Needs work » Needs review

The other issue should be ready, I also update the patch and used the dynamic way you suggested to get the status ids.

Patch at github

rszrama’s picture

Status: Needs review » Fixed

Alrighty, pulled this in and tested it. I also added support for deleting line items. I think these may need to be optimized and could benefit from the use of entity_metadata_wrapper(), but we can let it sit for now and see if any other edge cases turn up.

https://github.com/rszrama/drupalcommerce/commit/10a834cbd377a2f0849ec10...

googletorp’s picture

Status: Fixed » Needs review

I took a look at what you did and improved the code a bit.

For starters, we shouldn't check orders that already have the 'cart' status since no change will be made. I had this in originally but must have deleted it in a rewrite.

Another thing is, that if we want to handle cases where line items are deleted, we should not only update the order, but we should also remove the line item from the order as well. I did this using the entity wrapper, but for this to work, I needed to use a custom setter callback for line items since the default provided by entity module doesn't work.

Patch at github

rszrama’s picture

Your first change is a no brainer and a good catch. I think you had it originally, or at least my subconscious thinks I saw that code before. ; )

For the second, though, I don't think the Cart module should be responsible for cleaning up a line item reference field's data when the line item is deleted. In fact, this is already happening in commerce_line_item_field_attach_delete() in the Line Item module. (Funny enough, last month I thought it was happening because of the foreign key declaration, but it was just because I'd already written other code to handle the cleanup.)

Any way to get a follow-up commit to this branch that removes the line item removal code and just keeps the cart if check?

googletorp’s picture

Sure thing, I created a new commit at github

I created a new issue for the bug I also fixed allowing to set line_items values on a wrapped order object #1015574: Can't set line items on entity_metadata wrapped orders

damien tournoud’s picture

Status: Needs review » Needs work

Hm. Frankly, this whole idea makes me uncomfortable.

If anything all this should be rules-based, we should not make assumptions like this in the cart module.

Could we please remove all these hooks and replace them by rules?

googletorp’s picture

What is there to gain by making this into a rule?

Unless I'm mistaken, If this is made into a rule, it would be possible to disable it. Right now it's not a huge loss but when we get a shipping system this will most likely be business critical. The reason is that shipping needs to be calculated before the review/payment step to allow customers to see the shipping cost and totals. But as it is implemented now, users can still put items into the cart when the order is at review, which will require a new shipping calculation.

I can imagine that there could be other functionality that will have the same problem.

That's my 2 cents.

damien tournoud’s picture

It's precisely when we start hardcoding business assumptions that we fail. I can imagine *a lot* of use cases where changing the order status back to 'cart' is just not the right thing to do. For example: upsell during the checkout.

If I understand your use-case correctly, you want to make sure that "if the cart has been modified *after* the shipping calculation has been done, the order status is reset to the page of the shipping". This is completely different from the rule you got implemented. Nothing says that shipping calculation happens on the 'cart' stage. Nothing says that the order needs to be unconditionally reset to 'cart' as soon as it gets modified.

Could we please just revert all this and come up with a better solution? As far as I'm concerned this is currently a net regression is flexibility.

rszrama’s picture

Mmm, true - thanks for the heads up, Damien. Perhaps this should've been tied more specifically to the Add to Cart form instead of just any old line item / order->line_items change. The only redeeming thing I see here is that it limits the status reset to orders in a status w/ the cart property set to TRUE (i.e. by default that's the initial checkout form step and the review step).

At present we can't write the exact some logic using Rules, because of our use of EntityFieldQuery to find orders referencing a particular line item in commerce_cart_commerce_line_item_presave(). We can turn that whole bit into an action I suppose. : ?

Perhaps we should instead consider using the Cart Rules events (plus add one for updating an item in the cart and one for removing an item from the cart) and say that we're really going to reset the status when those events occur? Really what we're going after is inappropriate user action, not administrator / code action... but that still might not be delicate enough if an upsell module decides to integrate commerce_cart_add_to_cart_form() on any but the first step of checkout. However... I suppose someone could just add a condition to exempt particular checkout steps if necessary as I think Damien hinted above?

rszrama’s picture

Just a minor note to say that I've begun working toward an alternate solution by changing the add to cart form behavior a bit... basically we needed an API and Rules events for altering the products on a shopping cart order. Still more work to do here, but it's a start toward a purely Rules solution.

rszrama’s picture

Issue tags: +beta blocker

Tagging.

rszrama’s picture

Status: Needs work » Fixed

Alrighty, I removed the presave / delete hooks from the Cart module. They've actually been the source of a couple trickier bugs involving infinite loops since we were loading an order in a presave hook for orders... it was messy. There is now a default rule that resets the cart order status when a product is added to or removed from the cart. Additionally, since the cart module explicitly controls the cart form, there's a reset baked into the update cart button to "place" the order on that page and one for the checkout button to "place" it on the first checkout page (instead of cart, since Checkout doesn't depend on Cart).

I believe this brings a lot of sanity to the process and is obviously less buggy than our existing solution.

Commits:
https://github.com/rszrama/drupalcommerce/commit/9e932b53300a5cd4d89df50...
https://github.com/rszrama/drupalcommerce/commit/593688973394de683c6495a...

Status: Fixed » Closed (fixed)
Issue tags: -beta blocker

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