Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling of active orders when price of variants is changed. #664

Closed
gauravagrawal1710 opened this issue Jan 22, 2021 · 4 comments
Closed

Comments

@gauravagrawal1710
Copy link

Is your feature request related to a problem? Please describe.

  1. Price of a variant is modified.
  2. Existing active orders are not modified to include the new price.
  3. When an additional item is added, it gets added to the same OrderLine. This is incorrect handling, causes confusion to the client.
  4. No option to purge older active orders with old prices.

Describe the solution you'd like

  1. If the price is modified, the new item should create a new OrderLine. And add a flag to the existing orderLine that it is an older price.
  2. Multiple options to the store operator to handle existing active orders.
    a. Allow customers to purchase the variants they have in the cart, at the old price. (The flag mentioned above will help in handling in the frontend)
    a. Purge all existing active orders which contain that variant OR Remove that variant from all existing active orders.
    b. Update the existing active orders with the new price.

Describe alternatives you've considered
Till the time this is implemented, you can check if the line.unitPriceWithTax is equal to the current variant.priceWithTax. If not, customers can be intimated that older price is active for them.
If you want to stop customers from purchasing at the older price, delete those orders from the DB.

Additional context
Add any other context or screenshots about the feature request here.

@michaelbromley michaelbromley added this to the v1.0.0 milestone Jan 26, 2021
@michaelbromley
Copy link
Member

michaelbromley commented Feb 5, 2021

How are other frameworks/stores handling this?

It is surprisingly difficult to find discussions of this issue on the trackers of OSS e-commerce projects, as well as in the wider e-commerce context. Here's what I found:

Exploration of problem

Scenario 1

  • Customer adds Product A to order at $10. Does not checkout.
  • Admin changes price of Product A to $15.
  • Customer returns to storefront, adds a second Product A to order (now listed at $15).

Possible handlings

  1. Add the 2nd Product A and set the price of both OrderItems to $10
  2. Add the 2nd Product A and set the price of both OrderItems to $15
  3. Keep the original OrderLine at $10 and add a second OrderLine at $15

Scenario 2

  • Customer adds Product A to order at $10. Does not checkout.
  • Admin changes price of Product A to $15.
  • Customer returns to storefront, increases the quantity of Product A in cart from 1 to 2.

Possible handlings

  1. Keep the price of both at $10
  2. Update the price of both to $15
  3. Create a second OrderLine at $15

Scenario 3

  • Customer adds Product A to order at $10. Does not checkout.
  • Admin changes price of Product A to $15.
  • Customer returns to storefront, does not alter order, attempts to check out

Possible handlings

  1. Allow checkout at $10
  2. Change price to $15, display warning

Implementation

We could define a strategy which controls how these scenarios are handled. Rough sketch:

export type PriceCalculationResult = {
    price: number;
    priceIncludesTax: boolean;
};

export interface ChangedPriceHandlingStrategy {

  handlePriceChange(ctx: RequestContext, 
      current: PriceCalculationResult, 
      existingItems: OrderItem[]): PriceCalculationResult | string;

}

The idea is that any time an OrderLine is changed (item added, quantity increased) we check the price (as returned from the configured OrderItemPriceCalculationStrategy, which defaults to the ProductVariant price) and compare it to any existing OrderItems in the line.

If the calculated price does not equal the OrderItem.listPrice, then we invoke the handlePriceChange method to decide what to do.

Example: to use the existing price already in the Order:

  handlePriceChange(ctx, current, existingItems) {
    return { 
      price: existingItems[0].listPrice,
      priceIncludesTax: existingItems[0].priceIncludesTax,
    }
  }

Example: overwrite with the latest price:

  handlePriceChange(ctx, current, existingItems) {
    return current;
  }

The string return value could be used to display some kind of error or warning. Not totally sure how that would fit into the GraphQL API yet.

Additionally, the entire Order could be checked in this way when transitioning to the ArrangingPayment state, to cover scenario 3 above.

@michaelbromley
Copy link
Member

In my initial implementation, there are a couple of new fields on the OrderLine type:

type OrderLine {
    """
    Non-zero if the unitPrice has changed since it was initially added to Order
    """
    unitPriceChangeSinceAdded: Int!
    """
    Non-zero if the unitPriceWithTax has changed since it was initially added to Order
    """
    unitPriceWithTaxChangeSinceAdded: Int!
}

The value of these fields are calculated using a new DB column on OrderItem, initialListPrice. This gets set when the OrderLine is created, and allows us to track any changes in price since that time.

The new ChangedPriceHandlingStrategy (which is mostly identical to the sketch above) is fired when calling the addItemToOrder or adjustOrderLine mutations, in the case that the latest price differs from the initialListPrice.

Refreshing all prices

One part that is not yet implemented is the ability to refresh (update the prices of) all OrderLines in an Order. At first I was thinking of triggering this when e.g. transitioning to the ArrangingPayment state. But now I think it might be better to define a dedicated mutation which can be called on-demand. This would allow something like this to be implemented in the storefront:

  1. Customer arrives on storefront. Has existing session with active Order
  2. Storefront checks the date of the last updates to the Order
  3. If they are greater than , then call updateOrderPrices to refresh all the prices of each OrderItem, updating them to the latest value and calling ChangedPriceHandlingStrategy.handlePriceChange() where needed.
  4. The storefront can then find any OrderLines with a non-zero unitPriceWithTaxChangeSinceAdded value, and display a notice informing the customer that the price has changed since the item was added to the Order.

michaelbromley added a commit that referenced this issue Feb 9, 2021
Relates to #664

BREAKING CHANGE: The OrderItem entity has a new field, `initialListPrice`, used to better
handle price changes to items in an active Order. This schema change will require a DB migration.
@BeepLoveKarki
Copy link
Contributor

Let us also allow adding any custom algorithmic implementation in the handlePriceChange method by implementing any other services or even custom logics by the developer.

@michaelbromley
Copy link
Member

@BeepLoveKarki yes, will be supported - the ChangedPriceHandlingStrategy is an InjectableProvider so you can inject via the init() method.

@michaelbromley michaelbromley removed this from the v1.0.0 milestone Jun 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants