Mark a shipment as canceled if all the inventory units have been canceled
Summary
I noticed that if a multi-shipment order goes through and you cancel one of the shipments, an order recalculation will set the shipment as pending/ready. See:
https://github.com/solidusio/solidus/blob/main/core/app/models/spree/shipment.rb#L208
This is because it's only checking if the order is canceled. While this is a correct method to check if a shipment should be marked as canceled, it falls short if it's only 1 shipment in an order that needs to be canceled out of many. If I cancel a shipment, then the shipment will be marked as shipped and that can be quite surprising for customers and admins.
Instead, let's increase the guarantee on canceling a shipment for now by checking if the order is canceled or all of the inventory units for said shipment have been canceled.
I've also updated the logic for checking if the state should be set to shipped. While the check for shipped? remains to ensure immutability, we also mark a shipment as shipped just like in the Spree::OrderCancellations if some of the inventory units are shipped and some are canceled. See:
https://github.com/solidusio/solidus/blob/main/core/app/models/spree/order_cancellations.rb#L121
In the future, I think it would be better that when a shipment is canceled, an inventory unit cancellation should be done on the inventory units it's responsible for. That would help create a guarantee for resolving the bug highlighted above but this is a much larger change. For now, just marking a shipment as canceled when the inventory units have been canceled and matching the existing order cancelation logic is progress in the right direction.
Checklist
Check out our PR guidelines for more details.
The following are mandatory for all PRs:
- [x] I have written a thorough PR description.
- [x] I have kept my commits small and atomic.
- [x] I have used clear, explanatory commit messages.
The following are not always needed:
Codecov Report
Merging #5220 (3bde83e) into main (5f9c960) will decrease coverage by
0.01%. The diff coverage is100.00%.
@@ Coverage Diff @@
## main #5220 +/- ##
==========================================
- Coverage 88.67% 88.66% -0.01%
==========================================
Files 564 564
Lines 13894 13898 +4
==========================================
+ Hits 12320 12323 +3
- Misses 1574 1575 +1
| Impacted Files | Coverage Ξ | |
|---|---|---|
| core/app/models/spree/order_cancellations.rb | 95.74% <100.00%> (-2.04%) |
:arrow_down: |
| core/app/models/spree/shipment.rb | 93.52% <100.00%> (+0.07%) |
:arrow_up: |
:mega: Weβre building smart automated test selection to slash your CI/CD build times. Learn more
Thanks for working on this @BenMorganIO! I tried this locally with @MassimilianoLattanzio, who worked on something similar for one of our stores.
We think this works, but we are not sure about if it's a good thing to consider a shipped shipment as canceled if all of its content has been canceled. E.g. I ship an empty package by mistake, so I will cancel all the items in it. Is it correct to consider that shipment canceled, even if it has been shipped? Yes, there's always the carton that represents the shipped shipment, but maybe in this case it's not clear what actually happened looking at the order's shipment state. What do you think?
Also, I think there was another issue (already there, not related to this PR), because we are updating the shipment date of the shipment after canceling one of the items present in a shipped shipment, so in that case, the shipping date will represent the "all-items cancelation" date.
If a shipped shipment has all of its items marked as cancelled, it should remain shipped since the shipped state is supposed to be immutable.
But I don't see this happening with your code. Maybe I'm missing something?
@BenMorganIO let me show that with a quick video (I'm using your branch):
https://github.com/solidusio/solidus/assets/167946/407c0ad6-b20b-4b28-a88c-c4c7c92e561b
@kennyadsl I can't understand why someone would cancel all of the items in a shipment after it has been shipped. I think a return would be started if that was the case. However, I want to keep the code so that once a shipment has been shipped, it is effectively immutable and should not transition to canceled.
I can't understand why someone would cancel all of the items in a shipment after it has been shipped. I think a return would be started if that was the case.
You are right; it's hard to find a use case that resonates with this and the most realistic thing I could think of is having two items in the package (e.g., two glasses), both broken. Probably, you don't want to make a return for broken items, and canceling them would work. Do you think it's something worth taking into account?
@kennyadsl I think if we keep shipped shipments retained as shipped, then it shouldn't be an issue. I can see a customer returning all items from a shipment or an admin canceling the items if the shipment was lost or something. Either way, we do have a comment in the code that the shipped state is supposed to be immutable.