Non draft orders are refreshed when editing shipments

Created on 20 December 2022, over 2 years ago
Updated 17 January 2023, over 2 years ago

Problem/Motivation

Whenever a shipment is added, edited, or removed the order is refreshed
https://git.drupalcode.org/project/commerce_shipping/-/blob/8.x-2.x/src/...

While this is fine for draft orders and carts it can lead to data loss when the order is refreshed for placed orders. Placed orders should not change unless expressly done by an administrator. The order refresh causes line item prices to be updated, promotions to be reevaluated, taxes recalculated.

When shipments are changed there are three things that do need to happen on placed orders:

  1. The shipping adjustment total on the order needs to be updated to reflect the total cost of the shipments
  2. The shipping promotions need to be recalculated. Other promotions need to remain untouched.
  3. The tax for shipping, if applicable, needs to be recalculated and an event fired for any tax integrations to subscribe to

Steps to reproduce

Add, edit, or delete a shipment using the Shipments tab UI in the order administration area after an order has already been placed.

Proposed resolution

Trigger a shipping specific order refresh on non draft orders.

Possible ways of doing this:
Call the provided functions that commerce_shipping defines for the order refresh to only refresh shipping specific parts of the order. Probably the wrong way.

Provide a shipment refresh service that handles the refresh similar to the core one but skips anything that is not related to shipping.

🐛 Bug report
Status

Active

Version

2.0

Component

Code

Created by

🇺🇸United States rhovland Oregon

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇺🇸United States rhovland Oregon

    Updated patch for latest version of module

  • Status changed to Needs review 11 months ago
  • 🇩🇪Germany Anybody Porta Westfalica

    Setting needs review for maintainer feedback on the general points.

  • 🇳🇴Norway zaporylie

    I decided to bump the severity to Critical as items can also be deleted from the cart if one of the availability checkers returns that the item is not available - see AvailabilityOrderProcessor => AvailabilityManager

  • 🇮🇱Israel jsacksick

    Definitely bad that the order is refreshed regardless of the order state. I don't believe there is a need to actually check the order state. The order is always refreshed on save if it's a draft order, so setting the refresh state shouldn't even be necessary, but I guess this makes it explicit and make the intent clearer.

    The new approach should work in most cases, however, not in all cases. The LateOrderProcessor which is normally invoked on order refresh transfers shipment adjustments to the order.

    So with this change, the shipment tax isn't properly recalculated / reapplied for example. I'm thinking we might need to invoke this logic ourselves...
    What we need to do is remove all prior shipping tax adjustments added to the order by the shipping tax type, and then somehow reapply them.

    Any other custom logic applying shipment adjustments is not going to be invoked...But, this would put us in a much better situation than the one we're currently in...

  • 🇮🇱Israel jsacksick

    We're likely missing an equilavent of the OrderRefresh for shipments, a ShipmentRefresh service, but it's not that simple as the shipment affects the order.

    The problem with the approach described in my previous comment is that it lives in the form and doesn't address adjustments made to the shipments over the API for example (Programatic updates, or updates via JSONAPI endpoints for example).

  • 🇮🇱Israel jsacksick

    Not sure what to do regarding taxes. We could check if there are tax types using the shipping tax type plugin then remove any prior shipping tax adjustments added to the order.
    But that assumes the shipping tax is applied by the shpping local tax type plugin, if a service is used for tax calculation, it wouldn't be invoked.

    And in other cases, a fee could be added to the shipping which potentially needs a recalculation as well?

    So I don't really know whether I should add code to perform a tax recalculation for the case where there were prior tax adjustments added by the Shipping tax type plugin or not? It probably is better than not doing any recalculation?

  • 🇳🇴Norway zaporylie

    I think it's really convenient, from the OMS point of view, to be able to create shipments and have the order updated accordingly. Having to manually add adjustments would be very inconvenient. Having price adjustments automatically set, but tax adjustments not, could somehow be worse, as it would give a false belief that everything is happening automatically until you realize taxes are not there.

    IMHO, this issue could potentially benefit from a change to the OrderRefresh flow that would allow running a subset of order pre-/processors on placed orders by introducing order refresh scopes. Scopes would be defined as tagged service attributes and if missing, in the current release cycle we can assume they are only meant to run for draft orders. However, we could provide scopes for placed or even completed orders and run a curated subset of processors that we know can be run deterministically, and the one copying shipping adjustment is a prime example of such a processor. Tax processor could potentially also be safe to run if we resolve taxes for a specific date (current vs placed).

  • 🇮🇱Israel jsacksick

    IMHO, this issue could potentially benefit from a change to the OrderRefresh flow that would allow running a subset of order pre-/processors on placed orders by introducing order refresh scopes. Scopes would be defined as tagged service attributes and if missing, in the current release cycle we can assume they are only meant to run for draft orders. However, we could provide scopes for placed or even completed orders and run a curated subset of processors that we know can be run deterministically, and the one copying shipping adjustment is a prime example of such a processor. Tax processor could potentially also be safe to run if we resolve taxes for a specific date (current vs placed).

    I don't think it's as simple as that. Processors are kinda dependent indirectly on each other... It's expected that taxes are calculated post promotions for example.

    Promotions cannot be reapplied on a placed order, but if quantities were adjusted, promotions should in a way, be recalculated as there would be a risk in bringing the total further down than expected. There are more examples, but it's a very tricky subject.

  • 🇮🇱Israel jsacksick

    We've decided internally to simply stop refreshing non draft orders for now, without trying to mess with the adjustments in the case the order isn't a draft, just to address this critical bug report until we address this properly.

    With that said, it could be that the tests are broken by the change now, let's see.

  • @jsacksick opened merge request.
    • jsacksick committed 7178582f on 8.x-2.x
      Issue #3328162: Stop refreshing non draft orders when saving shipments.
      
  • Now that this issue is closed, please review the contribution record.

    As a contributor, attribute any organization helped you, or if you volunteered your own time.

    Maintainers, please credit people who helped resolve this issue.

Production build 0.71.5 2024