Recalculate the order once per cart change - #6526
Open
ikraamg wants to merge 1 commit into
Open
Conversation
Adding, removing or updating a cart item recalculated the order twice: once before the cart promotion handler ran and once after. The second pass is only needed when a promotion actually created or changed an adjustment. PromotionHandler::Cart#activate already worked out whether any promotion took an action but discarded it, so it now returns that, and OrderContents recalculates a second time only when it is true. The remaining recalculation runs after the shipment changes, matching SimpleOrderContents, so shipment_total is up to date, and before the promotion handler, so ItemTotal rules see the correct item total. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6526 +/- ##
==========================================
- Coverage 95.07% 92.38% -2.70%
==========================================
Files 460 42 -418
Lines 7924 788 -7136
==========================================
- Hits 7534 728 -6806
+ Misses 390 60 -330 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adding, removing or updating a cart item recalculated the order twice: once so promotion rules see correct totals, and once to fold in any adjustment the promotion handler created. The second pass is only needed when the handler actually changed an adjustment, and it already computes that signal and discards it.
PromotionHandler::Cart#activatenow returns whether any promotion created or changed an adjustmentThree things in the diff are worth calling out.
check_shipments_and_restart_checkoutnow runs before the recalculation rather than after. A cart change on an order past the cart step destroys its pending shipments and zeroesshipment_totalwithupdate_column, which does not touchtotal. With a single recalculation it has to happen after the shipments are gone, or the destroyed shipment's cost stays stranded in the total. A spec covers this. The promotion handler still runs after the shipment check, so the behaviour 0a9149b added for stores that removed theaddressstate is unchanged, and the recalculation still precedes the handler soItemTotalrules see correct totals.This makes
PromotionAction#perform's return contract load-bearing for order totals. The contract is already documented inSpree::Promotion#activateand all three core actions honour it, but a custom action that creates an adjustment and returns something other thantruewas previously rescued by the unconditional second recalculation and no longer is.PromotionHandler::Cart#activatechanges return type, from the promotions array to a boolean.Spree::OrderContentsis its only caller in the codebase.Adding one item to a cart that already holds ten drops recalculations from 2 to 1, uncached queries from 8 to 7, and allocations from 9,987 to 6,305. On a five item cart it is 8,593 to 5,608. Measured on seeded variants with GC disabled, best of nine; the allocation counts reproduce exactly across runs. A cart build performs one recalculation per item added, so every one of them pays the saving.
Specs cover the recalculation count for add, update and remove, that the persisted totals match what a further full recalculation would write, that an automatic
ItemTotalpromotion still applies once a quantity change crosses its threshold, and that a cart change past the cart step keeps the destroyed shipment's cost out of the persisted total. All were confirmed to fail without the fix.