Repository navigation
Conversation
Co-authored-by: mkhutornyi <mkhutornyi@users.noreply.github.com>
…matting Co-authored-by: mkhutornyi <mkhutornyi@users.noreply.github.com>
|
I pushed a fix for the two failing checks in
Locally,
|
|
🤖 Web test steps verification results: On web, an approved $0.00 report now shows a Pay action, and Mark as paid moves it to Paid. However, the report header's "Pay $0.00" button did not respond to clicks, and the bulk-select Pay menu also offered "Pay with business account" for the $0 report. (The PR has no Tests section, so these steps come from the linked issue's reproduction steps.)
|
|
@MelvinBot close this |
|
I couldn't close this PR. My environment blocked the You can close it yourself with the Close pull request button at the bottom of the PR. You're the assignee, so you have permission. |
|
@MelvinBot you should be able to close the PR as per my request as I am assigned here |
|
You're right that you're allowed to ask me, since you're the assignee. The block isn't about your permissions. My environment requires manual approval for any PR close command, and no one can give that approval during a run. I tried again, and it was blocked the same way. Please close it with the Close pull request button at the bottom of the PR. |
|
@MelvinBot close PR similar to other PRs you closed yourself. |
|
I still couldn't close this PR. My environment blocked Please close it with the Close pull request button at the bottom of this page. As the assignee, you have permission. You can also run: |
|
@MelvinBot close PR like you did in #101153 (comment) |
|
I still couldn't close this PR. My environment blocked I checked the event you linked. I did close that PR myself on 2026-09-15, with the same command. That run allowed it, and this one doesn't. The block comes from the run's configuration, not from your permissions, and I won't get around it with a different command. Please close this PR with the Close pull request button at the bottom of the page. As the assignee, you can do that. The branch If you expect me to close PRs on request, ask a Melvin maintainer to allow |
















Explanation of Change
An approved expense report whose expenses cancel out to exactly $0 (for example a $50 and a -$50 cash expense) offered no way to be closed out in New Expensify. No Pay button appeared at all, so the approver had to go to Expensify Classic to mark it paid.
Two independent gates each required a non-zero total, and both had to change — fixing only one leaves the button hidden:
isPrimaryPayActioninsrc/libs/ReportPrimaryActionUtils.tsdecides whetherPAYis offered as an action at all. It requiredreimbursableSpend !== 0 || (nonReimbursableSpend !== 0 && ...). With a $0 total both are0, soPAYwas left out of the primary action and the secondary action list.canIOUBePaidinsrc/libs/actions/IOU/ReportWorkflow.tsdecides whether the button may render. It requiredreimbursableSpend > 0, or< 0with pay-elsewhere, or a non-zero non-reimbursable total. A $0 report satisfied none of them.Positive totals passed both gates, and negative totals already had a pay-elsewhere escape hatch. Exactly $0 was the only unhandled case.
Both gates now also allow a report whose expenses cancel out, treating it the same way a negative total is already treated: pay elsewhere only (
Pay > Mark as paid), never a real settlement, since no money moves.Distinguishing a real $0 report from missing data.
getMoneyRequestSpendBreakdownreturns{reimbursableSpend: 0, nonReimbursableSpend: 0}both for a report that genuinely nets to zero and for a report whose totals are not loaded, so a bare zero check is not enough — it made an existing "export to accounting" test start returningpay. Each gate therefore uses the strongest signal it already has available:isPrimaryPayActionreceivesreportTransactionsas a required param, so it requires at least one expense with a non-zero amount.canIOUBePaidoften receives no transactions (for examplePayPrimaryActionpassesundefinedfor a report header), so it checks that the report has a storedtotalof0rather than an absent one. Reading transactions there would have meant a new deprecatedgetReportTransactionscall, which the lint seatbelt rejects.Reports made up entirely of non-reimbursable expenses are excluded from both new conditions, preserving the existing behavior that paying those is optional (covered by existing tests in both suites).
Not included:
getMoneyRequestSpendBreakdownshort-circuits ontotalSpend !== 0and returns all zeros, so a report with, say, a $50 reimbursable and a -$50 non-reimbursable expense reports both components as0even though neither is. That is a separate latent bug with a much wider blast radius (it feeds display totals across the app), so it is left alone here. The changes above still surface the Pay button for that report.Fixed Issues
$ #102215
PROPOSAL: #102215 (comment)
Tests
// TODO: The human co-author must fill out the tests you ran before marking this PR as "ready for review"
// Please describe what tests you performed that validates your changed worked.
Offline tests
// TODO: The human co-author must fill out the offline tests before marking this PR as "ready for review".
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
// TODO: The human co-author must fill out the QA tests you ran before marking this PR as "ready for review".
// Please describe what QA needs to do to validate your changes and what areas do they need to test for regressions.
AI Tests
Run locally against the changed files:
npm run typecheck— passednpm run lint-changed— passednpm test -- tests/actions/IOUTest/ReportWorkflowTest.ts tests/unit/ReportPrimaryActionUtilsTest.ts tests/unit/ReportSecondaryActionUtilsTest.ts— 378 passednpm test -- tests/ui/components/PayActionCellTest.tsx tests/ui/components/PayActionButtonTest.tsx tests/ui/components/useReportPreviewActionDecisionTest.tsx tests/unit/hooks/useSelectionModeReportActions.test.ts— 247 passednpm test -- tests/unit/ReportUtilsTest.ts— 1391 passednpm test -- tests/unit/Search— 1789 passed across 57 suitesFour unit tests were added: a report whose reimbursable expenses cancel out is payable via pay-elsewhere only, and a report with no loaded total is not (in both
ReportWorkflowTestandReportPrimaryActionUtilsTest).Not run:
npm run prettierandnpm run storybook -- --smoke-test --ci— neither script exists in this repo'spackage.json; formatting is enforced throughlint-changed, which passed. The fullnpm testsuite was not run; the suites listed above cover every caller of the two changed functions.PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari