Skip to content

fix(TWO-25554): answer "is billing distinct" from the checkbox and the quote - #413

Draft
dgjlindsay wants to merge 1 commit into
doug/TWO-25554-panel-fixesfrom
doug/TWO-25554-panel-delta
Draft

fix(TWO-25554): answer "is billing distinct" from the checkbox and the quote#413
dgjlindsay wants to merge 1 commit into
doug/TWO-25554-panel-fixesfrom
doug/TWO-25554-panel-delta

Conversation

@dgjlindsay

@dgjlindsay dgjlindsay commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Draft: needs a product ruling, not a review pass. One commit, stacked on #414.

Changes how "is billing a distinct address" is answered, and that question has now been answered four different ways in four review rounds, each fix trading one error class for another. The next answer should be a decision rather than another iteration.

What it does

billingIsDistinct() becomes the active "same as shipping" checkbox unchecked, AND the quote holding a billing address whose cache key differs from the shipping address's. Today it is the billing company field's visibility.

That fixes a real false positive: a third-party re-render (Fire Checkout, Amasty) detaches the billing fieldset for an instant, so a visibility answer goes transiently false while neither the buyer's intent nor the quote has changed. With the seed routed on that answer, the quote's billing company gets written into the shipping identity — one panel's event changing the other panel's field.

billingRoleIdentity() takes no argument, so the resolver's choice and the seed's destination read one predicate and cannot disagree. The resolver itself is untouched: code-identical to staging, one JSDoc line aside.

Why it is not mergeable as it stands

It introduces a false negative of the same class. The billing panel's mount predicate remains the fieldset's visibility, so the two are still different questions.

On a saved-shipping-address, non-virtual, search-enabled checkout: the billing panel mounts, which blocks the tile, so the shipping panel has no mount at all — the billing panel is the only company control on the page. A buyer picks a company and billing captures it. The quote has not yet diverged from shipping, so billingIsDistinct() is false, and the resolver returns shipping's empty identity. The only capture on the page is discarded. Reproduced, not inferred.

The two candidate rulings

Arbitrate the mount on billingIsDistinct(), so the billing panel does not appear until the quote diverges. Coherent, but it hides a panel the buyer has explicitly asked for and puts a DOM-visibility dependency back in the other direction.

Or make billingIsDistinct() true when the active box is unchecked and the billing field is a live mount, with the quote as fallback only where no box exists. This treats an unchecked box plus a rendered billing field as the buyer stating they have a separate billing address, and the lagging quote as simply behind. This is the recommendation.

A note on the asymmetry either ruling inherits: re-checking the box is authoritative immediately, ahead of the quote, because that is the buyer's stated intent; unchecking becomes distinct only once the quote actually holds a second address. During that window billing's identity is empty, so the resolver falls back to shipping and nothing is lost — claiming "distinct" before a second address exists would simply be false.

Tests

53 suites / 942 tests on top of #414's 929, all green. The commit carries its own tests for the transient-fieldset case and for re-resolution on a quote notification with the checkbox untouched.

…e quote

The single authority behind both the resolver and billingRoleIdentity(): the
buyer's own "same as shipping" box on the ACTIVE payment method, and the
quote's cache-key comparison. A third-party re-render detaches the billing
fieldset for an instant, and a visibility read hands the other panel's field
the company the buyer just picked.

The billing panel's MOUNT predicate is unchanged — it is still the visibility
of the billing company field. On a saved-shipping-address, non-virtual,
search-enabled checkout that leaves the billing panel mounted as the only
control on the page while the quote has not yet diverged, so a pick made
there resolves to shipping's empty identity and is discarded. Awaiting a
product ruling; this commit is self-contained so it can be dropped or amended
on its own.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff (production JS in company-capture.js, company-capture-component.js, company-identity.js, company-search.js, company-search-panel.js, company-source-resolver.js, address-autocomplete.js, gateway_method.js/template, plus the extensive Test/Js and LayoutProcessorPluginTest.php updates). No security, data-corruption, crash, or breaking-API issues found.

Notable, non-blocking observations:

  • quoteHoldsDistinctBillingAddress() compares cache keys with != rather than !==; both sides are always strings from getCacheKey() so this is harmless today, but worth tightening if that ever changes.
  • writeAddressInto/revertAddressFormFields now key the write record on the panel's identity object via WeakMap, and every call site guards on !identity before writing — this looks correctly threaded through call sites in company-capture.js, address-autocomplete.js, and the tests exercising the "no owning identity" refusal path.
  • renderAddressNotice() uses textContent, not innerHTML, so no XSS surface from the notice copy.

The PR description itself flags that the tip commit (f980c94) still needs a ruling on which panel is authoritative for billingRoleIdentity()/mount timing, and that translations for the new "could not fill in this company's address" string are still English placeholders — both already called out as open items by the author, not new findings from this review.

No inline comments to add given the above.

Ready for human approval ✅

@dgjlindsay dgjlindsay changed the title fix(TWO-25554): panel-local notices, mount-move teardown, and billing's own write destination fix(TWO-25554): answer "is billing distinct" from the checkbox and the quote Sep 1, 2026
@dgjlindsay
dgjlindsay changed the base branch from staging to doug/TWO-25554-panel-fixes September 1, 2026 17:55
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

Successfully merging this pull request may close these issues.

1 participant