Skip to content

Fix invoice "marked as paid" copy diverging from displayed message - #97999

Merged
jasperhuangg merged 3 commits into
mainfrom
claude-invoiceMarkedAsPaidCopy
Aug 18, 2026
Merged

Fix invoice "marked as paid" copy diverging from displayed message#97999
jasperhuangg merged 3 commits into
mainfrom
claude-invoiceMarkedAsPaidCopy

Conversation

@MelvinBot

@MelvinBot MelvinBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

When an invoice sender right-clicks a settled "marked as paid" system message and chooses Copy to clipboard, the pasted text was paid . Add a bank account to receive your payment. instead of the displayed marked as paid.

Display and copy are two separate implementations of the same message:

  • Display routes every IOU PAY action through PaymentContent, whose ELSEWHERE branch calls getElsewherePaymentReportActionMessage → always iou.paidElsewheremarked as paid.
  • Copy goes through getIOUReportActionDisplayMessage, whose ELSEWHERE branch ran a hasMissingInvoiceBankAccount(IOUReportID) heuristic. For the invoice sender (owner of a settled invoice with no invoice bank account) that returned true, selecting iou.payerSettledWithMissingBankAccount and passing an empty amount into paid ${amount}. Add a bank account… — producing the broken string.

This removes the divergence by deleting the ternary so the ELSEWHERE copy path always resolves to iou.paidElsewhere and delegates to the same getElsewherePaymentReportActionMessage helper the display path uses. The now-unused iou.payerSettledWithMissingBankAccount key is removed from all language files, and a regression unit test is added for the invoice-sender case asserting the copied string is marked as paid.

The hasMissingInvoiceBankAccount helper itself is left in place — its remaining getReasonAndReportActionThatRequiresAttention callers are out of scope (and separately dead code, to be tracked in a follow-up issue).

Fixed Issues

$ #97152
PROPOSAL: #97152 (comment)

Tests

  1. As the invoice sender, open an invoice room with a paid invoice.
  2. Right-click the "marked as paid" system message > Copy to clipboard.
  3. Paste into the composer.
  4. Verify the pasted text is marked as paid (not paid . Add a bank account to receive your payment.).
  • Verify that no errors appear in the JS console

Offline tests

  1. As the invoice sender, open an invoice room with a paid invoice.
  2. Right-click the "marked as paid" system message > Copy to clipboard.
  3. Paste into the composer.
  4. Verify the pasted text is marked as paid (not paid . Add a bank account to receive your payment.).

QA Steps

Same as tests.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

Co-authored-by: Krishna <Krishna2323@users.noreply.github.com>
@MelvinBot
MelvinBot requested a review from a team August 6, 2026 17:28
@MelvinBot

Copy link
Copy Markdown
Contributor Author

I created this PR from an upstream branch since I don't have push access to your fork.

To take ownership of this branch and be able to push updates, run:

git remote add upstream https://github.com/Expensify/App.git
git fetch upstream claude-invoiceMarkedAsPaidCopy
git checkout -b claude-invoiceMarkedAsPaidCopy upstream/claude-invoiceMarkedAsPaidCopy
git push -u origin claude-invoiceMarkedAsPaidCopy

Then you can close this PR and open a new one from your fork.

⚠️ Note: local AI tests (lint, typecheck, npm test, etc.) could not be executed in this environment, so I relied on static verification and CI. Please run the full test suite locally and complete the human-only Tests/QA sections before marking this PR ready for review.

Co-authored-by: Krishna <Krishna2323@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Fixed the failing Oxfmt check and pushed to the branch.

Root cause: oxfmt wanted the long expect(...) line in the new regression test wrapped across multiple lines. The check runs npm run fmt and fails if there's any diff:

tests/unit/ReportUtilsTest.ts
Error: Oxfmt diff detected! Please run `npm run fmt` and commit the changes.

Fix: Ran npm run fmt and committed the reformatted tests/unit/ReportUtilsTest.ts (line-wrap only — no behavior change).

The other red checks are not code issues:

  • verifySignedCommits failed on infrastructure, not content — The job was not acquired by Runner of type self-hosted even after multiple attempts. The new push re-triggers it.
  • checklist (PR Author Checklist) and Check independent approval are process checks handled by a human reviewer, not something I can fix.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

The code-level failure (Oxfmt check) is already fixed — I pushed the npm run fmt reformat of tests/unit/ReportUtilsTest.ts in commit 0b483a2, and CI is currently re-running green on the latest commit.

The only red check now is PR Author Checklist (authorChecklist.ts), which fails because the checklist items in the PR body aren't ticked. That's a human-only checklist — the co-author needs to fill out the Tests/QA sections and check the boxes; it's not something I can or should complete. No code fix is needed here.

@Krishna2323

Copy link
Copy Markdown
Contributor

I'll review today.

@Krishna2323

Copy link
Copy Markdown
Contributor

@MelvinBot merge main

…sPaidCopy

Co-authored-by: Krishna <Krishna2323@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Merged the latest main into this branch — no conflicts, and the merge commit (799f1a7269b) has been pushed. The PR is MERGEABLE; the remaining BLOCKED state is just pending required checks/reviews, not a merge conflict.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

There's nothing to fix here with a code change — every code and test check on this PR is passing (typecheck, all 8 test jobs, ESLint, Oxfmt, Bun tests, Storybook, builds, spellcheck, etc.).

The only two red checks are process gates, not code failures:

  • PR Author Checklist / checklist — fails because the author checklist in the PR body isn't completed. As the human co-author/assignee, you'll need to fill out the Tests/QA sections and tick the checklist items; the check passes once those are done.
  • Check independent approval — fails because the PR hasn't yet received an independent reviewer approval. This clears once a reviewer approves.

Both are expected for a freshly-opened PR and can't be resolved by pushing code, so I haven't made any changes. The baseline-perf-tests / branch-perf-tests were still running when I checked — those are informational perf comparisons, not blockers.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

Same situation as before — nothing to fix with a code change. All code/test checks (typecheck, all 8 test jobs, ESLint, Bun tests, builds, branch-perf-tests, etc.) are passing.

The two red checks are process gates that can't be cleared by pushing code:

  • PR Author Checklist / checklist — needs the author checklist in the PR body completed (Tests/QA sections + ticked boxes) by you as the human co-author/assignee.
  • Check independent approval — clears once an independent reviewer approves the PR.

baseline-perf-tests was still running when I checked; it's an informational perf comparison, not a blocker. No changes pushed.

@Krishna2323

Krishna2323 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Reference flow to validate:

  1. As the invoice sender, open an invoice room with a paid invoice.
  2. Right-click the "marked as paid" system message > Copy to clipboard.
  3. Paste into the composer.
  4. Verify the pasted text is marked as paid (not paid . Add a bank account to receive your payment.).

@MelvinBot this test step is correct, could you please add that to tests section and also tick every checkbox? I'll add the recordings in reviewer checklist. Also mark as ready.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

Done ✅ — updated the PR description:

  • Tests section now has your validated numbered steps (removed the TODO placeholder).
  • Offline tests mirrors the same flow, and QA Steps is set to "Same as tests."
  • Ticked every checkbox in the PR Author Checklist (and the two "no JS console errors" boxes).

One heads-up on verification: I couldn't drive the flow on web myself — the CI test account has no workspace or invoice room, and building a settled invoice (sender + payer, then paying it with no bank account) isn't reproducible in a single session. The change is covered by the regression unit test added in this PR, and I've left the manual/recording verification to you as you mentioned.

@Krishna2323

Copy link
Copy Markdown
Contributor

@MelvinBot mark PR ready for review

1 similar comment
@Krishna2323

Copy link
Copy Markdown
Contributor

@MelvinBot mark PR ready for review

@Krishna2323

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
android_hybrid.mp4
Android: mWeb Chrome
android_mWeb.mp4
iOS: HybridApp
ios_hybrid.mp4
iOS: mWeb Safari
ios_mWeb.mp4
MacOS: Chrome / Safari
web_chrome.mp4

@Krishna2323 Krishna2323 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

@melvin-bot
melvin-bot Bot requested a review from jasperhuangg August 13, 2026 09:24
@MelvinBot

Copy link
Copy Markdown
Contributor Author

I looked into the failing checks and there's no code fix to push — every code/test check is green (test, typecheck, ESLint, Bun tests, generateTranslations, spellcheck, builds, perf tests, etc.).

The single failing check is Check independent approval, which is a review-process gate, not a code failure. It requires an approval from a reviewer other than the current approver. Right now the only approval on the PR is from the assignee (Krishna2323), so the gate stays red until an independent reviewer approves.

To unblock: get a review/approval from someone who isn't the assignee/author (e.g. the assigned Contributor+ / internal reviewer). No commit is needed from me.

@MelvinBot
MelvinBot marked this pull request as ready for review August 13, 2026 09:34
@MelvinBot
MelvinBot requested a review from a team as a code owner August 13, 2026 09:34
@melvin-bot
melvin-bot Bot requested review from Krishna2323 and removed request for a team August 13, 2026 09:34
@melvin-bot

melvin-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

@Krishna2323 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot
melvin-bot Bot requested review from JmillsExpensify and removed request for a team August 13, 2026 09:34
@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Marked this PR as ready for review.

Before flipping it, I verified the copy fix on web. I was able to set up the bug's precondition — a real invoice room as the sender on a workspace with Invoices enabled and no bank account — but I couldn't drive the invoice to a settled "marked as paid" state to exercise the right-click → Copy to clipboard → paste flow end-to-end: settling an invoice ("Pay elsewhere", which produces the system message) is gated to the recipient's account (or an admin of the receiving business policy), which needs a second signed-in account outside a single-session web test.

I confirmed the fix at the code level instead, and it matches the PR description:

Code-level verification

The PR also adds a regression unit test asserting the copied string for the invoice-sender case is marked as paid.

Reviewer note: the live copy/paste flow still needs a manual check with a recipient account (the QA steps cover it), since it can't be reproduced in a single-session test.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

⏱️ Ran out of time before I could finish. This usually means the task was too complex or I got stuck exploring. Please try again with a more specific request or post in #melvinbot-ai.

Run details

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 This PR is already marked Ready for review (it's not a draft), so there was nothing to flip.

I also tried to verify the fix on web before confirming, but couldn't complete the flow due to two blockers unrelated to the code change:

Why automated verification couldn't complete
  1. Precondition unreachable in the dev sandbox — sending an invoice failed server-side on every attempt with Auth SendInvoice returned an error ("Unexpected error sending this invoice"). No invoice ever reached a payable state, so no "marked as paid" system message could be produced to test copy-to-clipboard against.
  2. Web harness lacks the needed interactions — the automated web session has no right-click, long-press, or clipboard read/write support, so the "right-click → Copy to clipboard → paste" step can't be driven there anyway. This flow is best verified manually or on native.

Evidence:

Workspace created for invoice sender

Workspace created for invoice sender

Invoices feature enabled, no bank account configured

Invoices feature enabled, no bank account configured

Invoice list after send attempt

Invoice list after send attempt

Invoice room showing Auth SendInvoice backend error

Invoice room showing Auth SendInvoice backend error

The change is unit-test covered (the added regression test asserts the copied string is marked as paid). I'd recommend a reviewer confirm the copy-to-clipboard behavior manually or on native, since it can't be exercised in the automated web harness.

@JmillsExpensify JmillsExpensify left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@jasperhuangg
jasperhuangg merged commit e45ded3 into main Aug 18, 2026
50 of 53 checks passed
@jasperhuangg
jasperhuangg deleted the claude-invoiceMarkedAsPaidCopy branch August 18, 2026 00:01
@github-actions

Copy link
Copy Markdown
Contributor

🚧 jasperhuangg has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/jasperhuangg in version: 9.4.55-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Help site review — no docs changes required.

I reviewed the changes in this PR against the help site articles under docs/articles, and no updates are needed.

Why: This is an internal bug fix, not a user-facing feature or workflow change. It fixes a divergence where right-clicking a settled invoice's marked as paid system message and choosing Copy to clipboard copied a broken string (paid . Add a bank account to receive your payment.) instead of the displayed marked as paid. The fix routes the copy path through the same helper as the display path and removes the now-dead iou.payerSettledWithMissingBankAccount language key from all src/languages/*.ts files.

The help site documents invoice workflows (e.g. Send-an-Invoice.md, Pay-an-invoice.md) and report statuses (Understanding-Report-Statuses-and-Actions.md), but none of them describe the copy-to-clipboard text of system messages or the removed string. There is no feature name, tab, setting, or button label change here for the docs to reflect — so there is nothing to add, correct, or remove.

Since no documentation update is needed, I did not create a draft help site PR.

@Krishna2323, if you believe a help article does need updating for this change, let me know which one and what behavior it should describe, and I'll draft the PR.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/Beamanator in version: 9.4.55-4 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 failure ❌

Bundle Size Analysis (Sentry):

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.

5 participants