A summary of the problem is:
When a shop product manager deletes or hides any product, variant, or option and then publishes that change to the catalog, that product configuration disappears from the catalog. This causes various errors when someone attempts to add an item with that product configuration to any cart, or when someone does any action with a cart that already contains an item with that product configuration.
This is really a series of problems, some more serious than others, and some easier to solve than others. Let me try to break it up into individual questions.
Those are the business logic questions. The main related technical question that I need to answer is as follows.
(xformCartGroupToCommonOrder) All calculations on cart items (such as shipping rates, taxes, surcharges, etc.) rely on knowing the current price of the product configuration. The simple-pricing plugin looks to Catalog to find this, but this fails when the product config is no longer in the catalog. Which price do we use as current/active for an item that has been removed from the catalog? If we use the price directly, it may confuse product managers because usually price is something that you need to publish before it takes effect.
I'm tempted to say we should avoid this question altogether by deleting all cart items for a product configuration at the same time we remove that option or variant from the catalog. But this brings up a number of other issues:
If we allow cart items to exist after their related variant is removed from the Catalog, then our only choice is to rethink how simple-pricing works. It would likely need its own concept of a "current" published price, separate from the act of publishing to the catalog. (This would be similar to how the more advanced pricing engine plugin works.)
3.2.0
@kieckhafer @ticean @focusaurus @mikemurray @rosshadden
We could leave items in the cart and throw a visible error on the cart page "This item is no longer available", which prevents checkout until removed. That way it wouldn't be confusing for users.
My worry with supporting hidden products is I feel like it precludes us from intentionally removing items from a catalog, unless we had both "hidden" and "no really, not there" states.
Do we have to physically remove the items, or can we just ignore them until we make the order, and then just omit them? Perhaps displaying a message, and/or locking checkout until they're removed. It would be better to let this be eventually consistent when the user takes an action on their cart.
I feel like, telling the user something is no longer available, and keeping a lightweight record around, like the original name, would help them find a replacement if one is available.
As for accidentally publishing a deletion, other than being more careful with the publish button, we could better confirm when products/variants are going to be archived.
If you want an undo, then it could be as simple as, just undo the archive and republish, or more complicated like a full revert to a previous version.
If we want to do some sort of there-but-not-there state as @rosshadden and @mikemurray suggest, I think it would require adding a completely separate Cart.removedItems array to avoid breaking changes. Because CartItem.price is currently a required field, and this is the piece of info we would no longer know.
Two other benefits of Cart.removedItems:
Cart.removedItems to Cart.items listAnd I agree this may be better for the shopper versus just plain deleting the item, but it isn't any different in how and where we'd have to implement it. We'd still need to either synchronously or asynchronously find and adjust every cart during or right after a catalog publish that hides or deletes.
I think it almost certainly has to be async, which means there will still be errors of some sort trying to retrieve a price during the lag time. My thought at the moment is to work around this by falling back to priceWhenAdded, which is stored directly on the items, rather than throwing an error when price calculation fails. This can lead to incorrect calculations during checkout, but they'd be caught by the placeOrder code.
I don't think shoppers should be able to checkout or maintain items in the cart if they can't view the catalog data for those items, for any reason, hidden or deleted. I'll call these "invalid" items now.
This is tricky stuff but after a first read through my inclination would be toward @aldeed's suggestion of 'cart.removedItems' array. I do think it's more pragmatic to wait until issues are detected to do that transformation of the cart. So abandoned carts never need to be changed, and carts that try to proceed toward checkout get updated on the fly during price recalculation or whatever step of the process first detects invalid items in the cart, and temporary hide/show operations have no customer impact.
This has been a helpful discussion so far. Feel free to continue posting thoughts if you have them, but for now I'll take a stab at the "lazy detect" suggested by @ticean, which can either straight up remove them or move them to removedItems (or maybe missingItems is clearer?) array. I'll see how that goes and then post here if I encounter other unforeseen problems.
Closing because the proposed solution is merged now.
Most helpful comment
We could leave items in the cart and throw a visible error on the cart page "This item is no longer available", which prevents checkout until removed. That way it wouldn't be confusing for users.
My worry with supporting hidden products is I feel like it precludes us from intentionally removing items from a catalog, unless we had both "hidden" and "no really, not there" states.