Task: Fix default party dangling after a same-save party removal

Table of Contents

This page documents a task in the Add a default party per account with a quick-login checkbox story. It captures the goal, current status, acceptance, and any notes or results.

Goal

In the account edit dialog, staging a removal of the party currently selected as "Default Party" (without also changing the combo) must not leave the saved account with a default_party_id pointing at a party it's no longer assigned to.

Status

Field Value
State DONE
Parent story Add a default party per account with a quick-login checkbox
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-07-12

Acceptance

  • Saving an edit that both stages a party removal and leaves that same party selected in the "Default Party" combo clears the account's default (sends an empty default_party_id), rather than sending the about-to-be-removed party's id.
  • The post-save in-memory rebase (currentAccount_.default_party_id, AccountPartiesWidget::rebaseDefaultParty()) reflects what was actually persisted (cleared), not the combo's raw, stale selection — so the UI doesn't show a default that the server no longer has.
  • Saves that don't involve a party removal are unaffected — this is a targeted fix at the point the request is built, not a change to the ordering of party-add/remove/default-party requests within a save.

Plan

  1. Compute whether the combo's currently-selected default party is also in the save's staged pendingPartyRemoves_ list, at the same point onSaveClicked() already builds the other pending-change snapshots (before going async) — this is the one place that already has both pieces of state (the combo selection and the staged removals) at hand.
  2. If so, treat the outgoing default_party_id as cleared (empty string) instead of the stale selection — closes the gap without needing to reorder the removal commit ahead of the account-fields update (which would introduce its own race: the removal landing before the default-party validation runs would then make a legitimate same-save "assign a new default from an existing party" request fail if that party happens to be mid-removal in the same batch — not a real scenario here, but reordering has its own sharp edges, so a targeted computation at the request-building site is the smaller, more contained change).
  3. Thread the same computed value (finalDefaultPartyId) into the post-save completion handler, so the in-memory rebase (currentAccount_.default_party_id, partiesWidget_->rebaseDefaultParty()) matches what was actually sent/persisted rather than re-reading the combo's now-stale raw selection a second time.

Notes

  • This is a narrow, deliberately conservative fix: it does not change the relative ordering of the account-fields update vs. party add/remove commits within a save (see onSaveClicked()), only what value is computed for default_party_id when those two pieces of state conflict.

Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario, run through the QA Validation Runner panel) that verify this task. Link new ones here as they're created; the scenario doc itself links back via its "Verifies task" field.

Scenario State Notes
     

PRs

PR Title
#1518 [ores.qt] Fix default-party dangle after same-save removal

Review

# Comment summary File Decision Notes
1 needsAccountSave was computed before defaultBeingRemoved=/=finalDefaultPartyId and never folded them in, so the primary scenario (remove the default party without touching the combo, no other field edited) never sent the corrective update_account_request — the server kept the dangling default while the client optimistically rebased to nil, a new client/server desync AccountDetailDialog.cpp Accepted Reordered so pendingPartyRemoves=/=defaultBeingRemoved are computed before needsAccountSave, and folded defaultBeingRemoved into it: needsAccountSave is now isDirty_ OR defaultBeingRemoved OR hasPendingDefaultPartyChange().

Result

Fixed in AccountDetailDialog.cpp's onSaveClicked(): computes finalDefaultPartyId — the combo's selected default party, or nil if that same party is also staged for removal in pendingPartyRemoves_ — and uses it both for the outgoing update_account_request's default_party_id and for the post-save in-memory rebase (currentAccount_.default_party_id, partiesWidget_->rebaseDefaultParty()), instead of reading the raw combo selection at each point.

Verification: rebuilt ores.qt.admin.lib clean under linux-clang-debug-make; ran ctest --preset linux-clang-debug-make -R iamores.iam.api.tests, ores.iam.core.tests, ores.iam.service.tests all green (100% pass, 0 failed). Did not add an automated UI test for this specific combo-plus-removal interaction (no existing harness exercises AccountPartiesWidget=/=AccountDetailDialog interactively) — same manual-QA-gap caveat as the rest of this story.

Emacs 29.3 (Org mode 9.6.15)