fix: show the surcharge net or gross per the store's tax display setting - #418
Conversation
Every buyer- and merchant-facing surcharge amount was rendered net regardless of configuration, so a store set to show gross prices showed a net surcharge beside gross everything else. Net and tax were already snapshotted at each entity's creation time, so this is display-only: Service\Order\SurchargeDisplay reads the store's "Display Prices" switch for the cart and sales tiers and each renderer picks accordingly. "Both" emits the paired excl/incl rows core uses for Subtotal and Shipping. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
First pass on this PR. No unresolved review threads to triage.
Reviewed the display-mode plumbing across SurchargeDisplay, the checkout/quote total collector, the sales (order/invoice/creditmemo) block and PDF renderer, the cart-totals API plugin, and the new TermSurchargePreview service, plus the frontend JS/knockout wiring.
Notes (non-blocking):
- The "Both" mode in
Model\Total\Surcharge::fetch()andModel\Pdf\Total\Surchargereturns a list of two segment arrays from a single collector rather than one assoc array. This mirrors the patternMagento\Weee\Model\Total\Quote\Weee::fetch()uses for its own multi-line totals, soTotalsReader/TotalsConvertershould split it into two segments correctly — worth confirming with a manual checkout run in "Both" mode since it's the one behavior that can't be fully pinned by the PHP unit tests here (they assert the collector's own return shape, not what core's reader does with it). SurchargeTaxCalculator::resolveRateForQuote()reads the tax engine's own declared rate for a synthetic 100.0 basis rather than deriving anything from a ratio, so it doesn't run into thetax/netpitfall called out in AGENTS.md for the order/tax composition path — this is a preview-only estimate feeding the term chips, not the authoritative total.- New constructor dependencies (
SurchargeDisplayon the total collector, sales block, and PDF renderer) are all concrete, autowirable classes, so nodi.xmlchanges are needed and nothing here trips the "optional constructor arg isn't autowired" trap from AGENTS.md.
No security, data-loss, or breaking-API issues found. Test coverage (PHP unit + JS) looks thorough for the mode matrix (excl/incl/both) across every rendering surface.
Ready for human approval ✅
Per-term failure logging moved into TermSurchargePreview, and pin the predicate core's TotalsReader uses to split a multi-row fetch(). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Re-reviewed the full diff at the current head (a commit landed after the earlier pass here). No unresolved review threads to triage.
Traced the display-mode plumbing end to end again: SurchargeDisplay resolution → Model\Total\Surcharge::fetch() (single segment vs. paired two_surcharge/two_surcharge_incl list in "Both") → the sales block/PDF renderer → the cart-totals API plugin → the two term-preview webapi endpoints now unified behind TermSurchargePreview → the knockout surcharge.js model's displayedTermSurcharges(). Also checked SurchargeTaxCalculator::resolveRateForQuote() — it reads the engine's declared rate off a synthetic 100.0 basis rather than a tax/net ratio, so it stays clear of the composition-path pitfall in AGENTS.md, and it's explicitly preview-only (the authoritative total collector path is untouched by this addition). Confirmed the checkout-session tax value the new fetch() branch reads (getTwoSurchargeTax()) is set in collect()/cleared in the zero-surcharge path, so the "Both"/"Incl" segment values aren't reading a stale or unset field.
No security, data-loss, or breaking-API issues found on this pass either. The one item worth a manual checkout smoke-test before merge (carried over from the prior review, still applicable since Model\Total\Surcharge::fetch() is unchanged in that respect): confirm "Both" mode's two-segment array is split correctly by core's TotalsReader/TotalsConverter, since that's the one behavior the PHP unit tests can pin the collector's own output for but not core's consumption of it.
Ready for human approval ✅
Adversarial review — clean roundRound 1 found one real defect and one unverified assumption; round 2 found nothing new. Fixed in The "Both" two-segment return, verified rather than assumed. The open question was whether core actually splits a collector's list return into separate segments. An assoc array of scalars yields Checked and cleared:
Mutation-checked — each of these fails the suite: Out of scope, worth knowing: Review by Claude |
Every buyer- and merchant-facing surcharge amount rendered net regardless of the store's tax display setting, so a store configured to show gross prices showed a net surcharge beside gross everything else.
Display-only fix. Net and tax are already snapshotted at each entity's creation time, so nothing about totals arithmetic changes — the surcharge tax is already in the Tax line and the gross already in the grand total, exactly as core carries "Subtotal (Incl. Tax)" alongside a separate Tax row.
Service\Order\SurchargeDisplayresolves the mode fromtax/cart_display/price(checkout, cart, term chips) andtax/sales_display/price(order/invoice/credit-memo views, emails, PDFs), read throughMagento\Tax\Model\Configas core's own Subtotal and Shipping totals blocks do. Each renderer then picks net or gross; "Both" emits the paired excl/incl rows core uses for those same totals.The two term-preview endpoints now return
netandgrossper term plus the mode, so a chip can render either; their duplicated per-term loops collapse into oneService\Order\TermSurchargePreview.