We have multiple checkout panes that allow to select additional options which are added as line items to the order.
In case of validation errors, we were seeing strange behavior where the line item changes that we made in a submit callback were not there when the checkout form callback was called again.
After a lot of debugging, we found out that the reason for this is in commerce_checkout_order_validate() in combination with the form cache, commerce_order_load() is used to make sure that it has an up to date order object before it starts to validate and submit the various checkout panes. As the form has been submitted, it does not rebuild the form and therefor does not call the form build functions but directly invokes validate, still having the order object from the form_state cache in $form_state.
In case of a validation error, it rebuilds the form using the defined arguments in $form_state['build_info'], which still is the old, cached order object and in commerce_checkout_form(), that is assigned to $form_state again. So the checkout pane forms are called with the old commerce order and sad things happen if they try to do stuff with that (e.g. trying to load line items which were deleted).
In either of those two locations, we need to ensure that we do have the correct, up to date order object. Either by doing $form_state['build_info']['args'][0] = $order in validate after loading it or by doing a commerce_order_load($order->order_id) in commerce_checkout_form() (which is free on normal calls as it's already in the static cache).
| Comment | File | Size | Author |
|---|---|---|---|
| #1 | commerce-checkout-validate-order-reference-1968712-1.patch | 895 bytes | berdir |
Comments
Comment #1
berdirApplied patch is one of the suggested ways to fix this.
Comment #2
rszrama commentedIf I'm understanding your issue properly, it seems that we ought to reset the order in the form state at the end of the validate handler right after we save it, no?
Comment #3
berdirDoesn't matter where it happens within the validate callback, as long as we update the old object reference to the newly loaded one.
Comment #4
rszrama commentedAye aye. Well, I spent some time this morning duplicating the problem w/ a test using the Shipping module and forcing some order updates on failed validation. Worked as described in the original post and had the additional nefarious side effect of screwing up revision IDs when I forced a new revision in a failed validate handler.
The issue as I read it as that this is a side effect of saving the order before form submission, a case of premature feature addition to the code where we decided to catch all data as early as possible even if it broke form validate / submit convention. I'd say it's similar to the impulse that resulted in sell price pre-calculation being the default accommodation instead of an edge case, and lo and behold... it sits there unused by virtually everyone. : P
I'll write a blog post about it, castigate myself a bit.
I did move the snippet to happen after the order is saved.
Commit: http://drupalcode.org/project/commerce.git/commitdiff/fe19852