Skip to content

feat: add a Paid column option to the Spend reports table - #103956

Open
sam5235 wants to merge 9 commits into
Expensify:mainfrom
sam5235:sam5235/103650-paid-column
Open

sam5235 wants to merge 9 commits into
Expensify:mainfrom
sam5235:sam5235/103650-paid-column

Conversation

@sam5235

@sam5235 sam5235 commented Oct 14, 2026 •

Copy link
Copy Markdown

Explanation of Change

The Spend tables can show when a report was submitted and approved, but not when it was paid. This adds an optional Paid column to the Reports table and the Expenses table.

  • Column: TABLE_COLUMNS.PAID is added to the expense report columns after First approved, and to the expense columns after Approved (COLUMN_AVAILABILITY, Search only). It is off by default and uses the existing "Paid" label (search.filters.paid).
  • Date: getPaidDate uses the report's paid date from the Search response, or the latest payment action's date if that is newer. The action covers paying from Search (including offline), before the next search refreshes report.paid. It is blank unless the report status is Paid, and a payment that was later cancelled, dequeued or bounced does not count. A payment the submitter marked as received does count, because the report was still paid on that date.
  • Expenses: an expense shows its report's paid date, the same way it already shows the report's Submitted and Approved dates. Unreported expenses have no report, so the cell is blank.
  • New report key: paid is added to the Report type and registered in whitelistedReportKeys, DebugUtils and getStableReportSelector, next to approved (same as debitedAmount in [Payment due @huult] [NO QA] Add Amount debited and Amount reimbursed filters, columns and sorting #97708, also a Search-only report field).
  • Shared rule: the payment rules from getLastPaidAction (Paid by) now live in getPaymentAction (latest or earliest valid payment, ignoring cancelled ones) and isPaymentMarkedAsReceived, used by both Paid and Paid by. Paid by's behaviour is unchanged and covered by its existing tests. Paid takes the earliest "received" marking, matching the backend, and the latest real payment.
  • Width: Paid uses the same width as the other date columns: narrow, and wide only when a report's paid date is from a past year (shouldShowYearPaid, read from report.paid like Approved reads report.approved). getTableMinWidth counts it as a date column.
  • Sorting: Paid is mapped in expenseReportColumnNamesToSortingProperty and transactionColumnNamesToSortingProperty, so the header is sortable and Paid shows in "Sort by" when the column is enabled.

Backend change (details) is live on staging and production. Search returns paid on each report when the query asks for it (the Paid column is selected, or sorting by Paid) and accepts sortBy:paid, for both type:expense-report and type:expense.

Areas that could be affected: the Paid by column (shared helper), the Reports and Expenses table headers and rows on wide layouts, and the Sort by list.

Unrelated console error: while testing I saw The result of getSnapshot should be cached to avoid an infinite loop from ReportActionsListItemRenderer on web and iOS. It also appears on the sign-in screen before any report code runs, and this PR doesn't add or change any store subscriptions, so it isn't caused by this change. Happy to open a separate issue for it.

Unrelated bug: if a report is approved and then paid in the same session, its Approved date goes blank until reload. It happens on main too: getApprovedDate only uses the APPROVED action while the status is Approved, and approving doesn't set report.approved optimistically.

Fixed Issues

$ #103650
PROPOSAL: #103650 (comment)

Tests

Precondition: an account that is admin of a workspace, with paid expense reports paid on at least two different days, and one approved expense report that has not been paid yet.

Reports table

  1. On web (wide layout), go to Spend > Reports.
  2. Verify the table has no Paid column.
  3. Click Display > Edit columns.
  4. Verify Paid is in the list and is not ticked.
  5. Tick Paid (leave Paid by unticked), then click Save.
  6. Verify a Paid header appears, paid reports show their paid date, and the cell is empty for the report that is approved but not paid.
  7. On that report click Pay > Mark as paid.
  8. Verify the Paid cell shows today's date straight away.
  9. Refresh the page.
  10. Verify the Paid date is still shown.
  11. Click the Paid column header, then click it again.
  12. Verify the reports are ordered by paid date, ascending then descending, and reports with no paid date are grouped at one end.
  13. Click Display > Sort by.
  14. Verify Paid is in the list, select it and switch between ascending and descending, and verify the order changes the same way.
  15. Open Display > Edit columns, untick Paid, click Save.
  16. Verify the Paid column is removed.

Expenses table

  1. Go to Spend > Expenses and click Display > Edit columns.
  2. Verify Paid is in the list, after Approved, and is not ticked.
  3. Tick Paid and click Save.
  4. Verify expenses on paid reports show their report's paid date, and expenses on unpaid reports and unreported expenses show an empty cell.
  5. Click the Paid column header, then click it again, and verify the expenses are ordered by paid date, ascending then descending.
  6. Click Display > Sort by and verify Paid is in the list.
  7. Untick Paid in Edit columns and verify the column is removed.

Narrow layout

Edit columns is only available on wide layouts, so mobile uses columns from a saved search created on web.

  1. On web (wide layout), go to Spend > Reports, tick Paid in Display > Edit columns, and click Save.
  2. Add a filter (e.g. Status: Paid) and save the search as Reports with Paid.
  3. Go to Spend > Expenses, tick Paid in Display > Edit columns, and click Save.
  4. Add a filter (e.g. Status: Paid) and save the search as Expenses with Paid.
  5. On a narrow layout (mobile), open the saved search Reports with Paid, click Display > Sort by, and verify Paid is in the list.
  6. Select Paid and switch between ascending and descending. Verify the card order changes, and the cards look the same as before, with no paid date on the card.
  7. Open the saved search Expenses with Paid and repeat steps 28 and 29.
  8. Mark an approved report as paid from the Reports list.
  9. Verify the card's status changes to Paid with no errors.
  • Verify that no errors appear in the JS console

Offline tests

  1. Enable the Paid column on Reports and on Expenses (steps 3 to 5 and 17 to 19 above).
  2. Go offline.
  3. On Spend > Reports, on an approved report click Pay > Mark as paid.
  4. Verify the Paid cell shows today's date while offline and the row is greyed out as pending.
  5. Go to Spend > Expenses and verify the expenses on that report show today's date in Paid.
  6. Go back online.
  7. Verify the row is no longer greyed out and the Paid date stays on both tables.

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 verified any copy / text that was added to the app 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 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 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.
  • If the PR adds or modifies the UI:
    • I asked an AI agent to review the changes for accessibility issues and addressed its findings.
    • I tested with a screen reader (VoiceOver on macOS) and verified all new/changed elements are reachable with a logical focus order.
    • I verified all new/changed elements have meaningful accessible names and roles.
    • I verified state changes are announced (e.g. checked/unchecked, expanded/collapsed, selected).
  • 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.Native.mp4

e5e9e-448d-4fff-920d-07494ce01649

Android: mWeb Chrome
Android.mWeb.mp4
iOS: Native
IOS.Native.mp4
iOS: mWeb Safari
IOS.mWeb.Safari.mp4
MacOS: Chrome / Safari
MacOS.Chrome.mov

Add an optional Paid date column for expense reports. The date comes from
the latest payment action that was not cancelled, since the Search response
has no paid date on the report. Share that rule with the Paid by column.
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@sam5235

sam5235 commented Oct 14, 2026

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@sam5235

sam5235 commented Oct 14, 2026

Copy link
Copy Markdown
Author

@Expensify/design adding an optional Paid column to the Spend > Reports table, matching the existing Approved and First approved columns: same header style and date format, and the same fixed width as First approved. It's off by default and enabled from Display > Edit columns. Videos are in the description. Anything you'd like changed?

exfy-clabot Bot added a commit to Expensify/CLA that referenced this pull request Oct 14, 2026
@sam5235
sam5235 marked this pull request as ready for review October 14, 2026 09:25
@sam5235
sam5235 requested review from a team as code owners October 14, 2026 09:25
@melvin-bot
melvin-bot Bot requested review from FitseTLT and garrettmknight and removed request for a team October 14, 2026 09:25
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T09:46:05.692084Z ab766b7 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@melvin-bot

melvin-bot Bot commented Oct 14, 2026

Copy link
Copy Markdown

@FitseTLT 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]

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab766b70bf

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/libs/SearchUIUtils.ts
@shawnborton

Copy link
Copy Markdown
Contributor

It seems like these two columns have different widths, is that correct?
CleanShot 2026-10-14 at 12 34 38 PM@2x

I think I would expect them to be the same since they are basically displaying the same kind of information.

Paid had a fixed wide width, so it didn't line up with Approved. It now
uses the narrow width like the other date columns and widens only when
a paid date is from a past year and needs to show the year.
@sam5235

sam5235 commented Oct 14, 2026

Copy link
Copy Markdown
Author

Good catch @shawnborton. Paid now uses the same width as Approved: narrow by default, and widening only when a paid date is from a past year and needs to show the year.

@shawnborton

Copy link
Copy Markdown
Contributor

Great, thank you!

Copy link
Copy Markdown
Contributor

Looking very good!

@sam5235 sam5235 changed the title [HOLD] feat: add a Paid column option to the Spend reports table [HOLD BE] feat: add a Paid column option to the Spend reports table Oct 16, 2026
@sam5235

sam5235 commented Oct 16, 2026

Copy link
Copy Markdown
Author

@FitseTLT this is ready for review. The HOLD is only for merging, until the backend change from #103650 (comment) is live. Design has already reviewed the UI changes.

@sam5235 sam5235 changed the title [HOLD BE] feat: add a Paid column option to the Spend reports table feat: add a Paid column option to the Spend reports table Oct 19, 2026
@sam5235

sam5235 commented Oct 19, 2026

Copy link
Copy Markdown
Author

@FitseTLT the backend change is live, so this is off hold and ready for review. Videos are updated, including sorting by Paid.

garrettmknight
garrettmknight previously approved these changes Oct 19, 2026

@garrettmknight garrettmknight 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.

Looks good from a product perspective.

@FitseTLT

Copy link
Copy Markdown
Contributor

We support Approved for expenses too why is Paid limited to expense reports @sam5235 doesn't the BE support it.

@sam5235

sam5235 commented Oct 19, 2026

Copy link
Copy Markdown
Author

This PR follows the issue scope, which is the reports table. Extending it to expenses is fairly straightforward. Expense rows already take Submitted and Approved from their report, so Paid can use getPaidDate the same way.

@FitseTLT @garrettmknight should I add it to the expenses table in this PR? I'll check that the backend supports it on expenses in the meantime.

@sam5235

sam5235 commented Oct 19, 2026

Copy link
Copy Markdown
Author

This PR follows the issue scope, which is the reports table. Extending it to expenses is fairly straightforward. Expense rows already take Submitted and Approved from their report, so Paid can use getPaidDate the same way.

@FitseTLT @garrettmknight should I add it to the expenses table in this PR? I'll check that the backend supports it on expenses in the meantime.

Checked the BE for expenses on production and It already supports Paid with type:expense, columns:paid returns paid on each report (blank for unpaid ones), and sortBy:paid is accepted. So adding it to the expenses table is straight forward.

Comment thread src/libs/SearchUIUtils.ts Outdated
Comment on lines +1559 to +1562

if (!ctx.shouldShowYearPaidReport && report && isPaidDateFromAPastYear(report, actions)) {
ctx.shouldShowYearPaidReport = true;
}

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.

Why do we add this different from approved? you are totally depending on actions data instead of the snapshots paid field which the spend page api is now returning.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point, I have changed it now.

The width now reads report.paid directly, like Approved reads report.approved, and isPaidDateFromAPastYear is removed. The cell still uses getPaidDate, so an offline payment shows right away, same as getApprovedDate.

Comment thread src/types/onyx/Report.ts
approved?: string;

/** The date the report was paid */
paid?: string;

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.

I still don't see paid field populated by the BE in reports

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Screenshot 2026-10-19 at 8 45 31 PM

The BE only returns paid when the query asks for it, when the Paid column is selected (columns:…,paid in the query) or when sorting by Paid. Otherwise the key isn't sent at all. It's also only in Search responses, not OpenReport/OpenApp.

To check, compare the Search response for type:expense-report columns:date,title,paid with plain type:expense-report. In the first one every report has paid (blank when unpaid), and in the second there's none.

created: unknown;
submitted: unknown;
approved: unknown;
paid: unknown;

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.

here?? too

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It's required by the type check at the bottom of this file testReportKeys, every key on Report must be listed here or typecheck fails. paid comes on the report_ entries of the Search snapshot, which are typed as Report (SearchResultDataType), so it has to be on Report and therefore here.

I think it's the same as debitedAmount/creditedAmount, which are also Search-only report fields.

Comment thread src/selectors/Report.ts
created: report.created,
submitted: report.submitted,
approved: report.approved,
paid: report.paid,

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.

this also might not be needed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It's needed for the type check.

The return object satisfies Record<keyof StableReport, unknown>, so every Report key must be returned or added to ExcludedFields. The doc comment above says new fields go in the return object, and only fields that change on every message/read go in ExcludedFields. paid only changes on payment, so it sits next to approved, same as debitedAmount.

@FitseTLT

Copy link
Copy Markdown
Contributor

This PR follows the issue scope, which is the reports table. Extending it to expenses is fairly straightforward. Expense rows already take Submitted and Approved from their report, so Paid can use getPaidDate the same way.
@FitseTLT @garrettmknight should I add it to the expenses table in this PR? I'll check that the backend supports it on expenses in the meantime.

Checked the BE for expenses on production and It already supports Paid with type:expense, columns:paid returns paid on each report (blank for unpaid ones), and sortBy:paid is accepted. So adding it to the expenses table is straight forward.

Yes apply

@sam5235

sam5235 commented Oct 20, 2026

Copy link
Copy Markdown
Author

@FitseTLT thanks for the review! All comments are addressed and Paid is now on the Expenses table too. The description, tests and videos are updated. Ready for another look.

Comment thread src/libs/SearchUIUtils.ts Outdated
@sam5235
sam5235 requested a review from FitseTLT October 20, 2026 18:44
@FitseTLT

Copy link
Copy Markdown
Contributor

BTW I saw that on mobile paid sortBy option is included for Reports but not Expenses. When I see Approved or Submitted they are both not shown in sort by option in Reports too I think we need to make it consistent for Paid too.

@FitseTLT

FitseTLT commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

There is a report that I took received payment action yesterday and today. the paid is yesterday before opening report (which is the paid field populated by the BE) then when the actions are loaded after opening it changes to today. so for received payment actions the actions calculation back need to take the oldest not the latest to align with BE logic.

App/src/libs/SearchUIUtils.ts

Lines 2717 to 2722 in 09246c6

return findActionByCreated(
candidates,
[CONST.REPORT.ACTIONS.TYPE.REIMBURSED, CONST.REPORT.ACTIONS.TYPE.MARKED_REIMBURSED, CONST.REPORT.ACTIONS.TYPE.MARK_REIMBURSED_FROM_INTEGRATION, CONST.REPORT.ACTIONS.TYPE.IOU],
'latest',
seed,
);

When a report is marked as received more than once, Paid shows the latest marking instead of the first — getPaidDate's lookup has no special ordering for isSubmitterMarkedPaymentReceived actions, so a later re-mark overwrites the original received date. Should probably take the earliest among received-marked candidates specifically (real payments can stay on latest).

@sam5235

sam5235 commented Oct 22, 2026

Copy link
Copy Markdown
Author

There is a report that I took received payment action yesterday and today. the paid is yesterday before opening report (which is the paid field populated by the BE) then when the actions are loaded after opening it changes to today. so for received payment actions the actions calculation back need to take the oldest not the latest to align with BE logic.

App/src/libs/SearchUIUtils.ts

Lines 2717 to 2722 in 09246c6

return findActionByCreated(
candidates,
[CONST.REPORT.ACTIONS.TYPE.REIMBURSED, CONST.REPORT.ACTIONS.TYPE.MARKED_REIMBURSED, CONST.REPORT.ACTIONS.TYPE.MARK_REIMBURSED_FROM_INTEGRATION, CONST.REPORT.ACTIONS.TYPE.IOU],
'latest',
seed,
);

When a report is marked as received more than once, Paid shows the latest marking instead of the first — getPaidDate's lookup has no special ordering for isSubmitterMarkedPaymentReceived actions, so a later re-mark overwrites the original received date. Should probably take the earliest among received-marked candidates specifically (real payments can stay on latest).

Fixed it!

A received marking also creates MARKED_REIMBURSED + MARKED_REDEEMED, so the latest-payment lookup picked up the second marking. getPaidDate now takes the earliest received marking and the latest real payment (cancelled ones still excluded), which matches the BE. The "received" check is shared with Paid by (isPaymentMarkedAsReceived).

Right after marking, MARKED_REDEEMED isn't stored yet, so the new marking still looked like a real payment. The flagged pay action now pairs it too, which also stops Paid by from briefly showing the submitter after a received marking (that happens on main as well). Added tests for a report marked as received twice, before and after its actions fully load.

✅ Received payment (payments off): A marked, then B, then A again. Sorted by Paid, A stays before B right after marking, after a cache clear and after opening A.

Payment.off.workflow.mp4

✅ Real payment: D paid, then C, then D's payment cancelled and D paid again. D uses its second payment, so C stays before D.

Real.Payment.workflow.mp4

@sam5235

sam5235 commented Oct 22, 2026

Copy link
Copy Markdown
Author

BTW I saw that on mobile paid sortBy option is included for Reports but not Expenses. When I see Approved or Submitted they are both not shown in sort by option in Reports too I think we need to make it consistent for Paid too.

Sort by follows the visible columns of the current query, so Paid should behave like Approved and Submitted. What I'm seeing is that on the mobile app, Reports picks up the columns selected on desktop but Expenses opens with the default columns, so the extra columns (Paid included) don't show in its Sort by.

I believe this already happens on main/staging and isn't from this PR, since Approved should behave the same way there. I'm confirming it and will update here with sample video recordings.

Comment thread src/libs/SearchUIUtils.ts
@sam5235

sam5235 commented Oct 23, 2026

Copy link
Copy Markdown
Author

BTW I saw that on mobile paid sortBy option is included for Reports but not Expenses. When I see Approved or Submitted they are both not shown in sort by option in Reports too I think we need to make it consistent for Paid too.

Sort by follows the visible columns of the current query, so Paid should behave like Approved and Submitted. What I'm seeing is that on the mobile app, Reports picks up the columns selected on desktop but Expenses opens with the default columns, so the extra columns (Paid included) don't show in its Sort by.

I believe this already happens on main/staging and isn't from this PR, since Approved should behave the same way there. I'm confirming it and will update here with sample video recordings.

Confirmed this on main (local build, staging server), using Approved since main has no Paid column:

  • Reports: columns selected on desktop (Chrome) show up in the mobile app's Sort by after signing in again.
  • Expenses: columns selected on desktop never show up in the mobile app's Sort by. It keeps the default options.

So the Expenses Sort by on mobile not picking up the desktop columns is existing behavior on main, not from this PR. Paid follows the same implementation as Approved, which is why it shows in Reports' Sort by and not in Expenses'.

Reports on main branch

main-branch-reports.mp4

Expenses on main branch

main-branch-expenses.mp4

Happy to open a separate issue for the Expenses one if you think it's worth fixing.

@sam5235
sam5235 requested a review from FitseTLT October 23, 2026 02:03
@FitseTLT

FitseTLT commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

You're theory doesn't work for me I added paid in Reports in web but it is not available in mobile.

2026-10-23.18-45-35.mp4

@FitseTLT

Copy link
Copy Markdown
Contributor

Okay it works after signing out/in but the theory applies for Expenses tab too. So you should update the test steps accordingly.

@FitseTLT

FitseTLT commented Oct 23, 2026 •

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: mWeb Chrome
iOS: HybridApp
iOS: mWeb Safari
MacOS: Chrome / Safari
2026-10-23.19-54-56.mp4

@FitseTLT

Copy link
Copy Markdown
Contributor

@JS00001 the sortBy column options for mobile is totally random. there is a theory by @sam5235 that the columns open in desktop appears if sign out and in for mobile but that's not consistent from my testing. In my testing IOS web aligned with the theory but for android and android web it didn't there is no way it would be platform specific I think there is some problem in BE because there is nothing responsible for that in FE. So we can investigate it separately but I don't think we should hold this pr for it. WDYT

@sam5235

sam5235 commented Oct 24, 2026

Copy link
Copy Markdown
Author

@JS00001 the sortBy column options for mobile is totally random. there is a theory by @sam5235 that the columns open in desktop appears if sign out and in for mobile but that's not consistent from my testing. In my testing IOS web aligned with the theory but for android and android web it didn't there is no way it would be platform specific I think there is some problem in BE because there is nothing responsible for that in FE. So we can investigate it separately but I don't think we should hold this pr for it. WDYT

I agree with this one. Upon further investigation in the apps with the same account, my earlier explanation doesn't capture the full picture of how Sort by behaves on mobile:

  1. Saved searches (Reports and Expenses): the most reliable. When a saved search includes the Paid column, Paid shows in Sort by on mWeb and in the apps every time.
  2. Reports tab: usually picks up the web selection after signing out and back in, or clearing the cache.
  3. Expenses tab: the least predictable. It often reopens with its last local query, or the default one, so the web selection doesn't reach mobile.

This is the same on main with Approved, so it isn't specific to Paid. I've based the narrow layout test steps on saved searches, since they give the same result on every platform.

It might be worth agreeing on a clearer, predictable rule for when a column and Sort by selection made in one session is saved beyond the local cache and picked up by the user's other sessions (on sign-in, cache clear, or switching tabs). That would make this behavior easier to rely on in implementation and testing.

@sam5235

sam5235 commented Oct 24, 2026

Copy link
Copy Markdown
Author

Okay it works after signing out/in but the theory applies for Expenses tab too. So you should update the test steps accordingly.

👍🏻 Thanks, I have updated the test steps in the PR description.

This branch has not been deployed

No deployments
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.

6 participants