You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[$175] Approved date column is not populated for that expense that has a proper approved date. #104768
Version Number: 9.5.16-1 Reproducible in staging?: Y Reproducible in production?: Y If this was caught during regression testing, add the test name, ID and link from BrowserStack: Email or phone of affected tester (no customers): Logs:https://stackoverflow.com/c/expensify/questions/4856 Expensify/Expensify Issue URL: Issue reported by:@FitseTLT Slack conversation (hyperlinked to channel name): #Expensify Bugs
Action Performed:
Open spend > Expenses and add Approved column on the table and also filter with status Paid
Find an expense that is approved and take the report id of the parent expense report.
Now go to the expense report in inbox
Clear cache and directly go to the inbox to open the expense report before opening spend page.
Go to Spend page
Expected Result:
Approved date column populated
Actual Result:
Approved date column is empty and not populated
Workaround:
Unknown
Platforms:
Select the officially supported platforms where the issue was reproduced:
On the Expenses view, Search builds each row from the live Onyx report when one exists, not from the Search snapshot report. Opening the report in Inbox first creates that live report.
The live report from OpenReport doesn't carry approved. The App never sets that field itself; only the server sends it.
getApprovedDate only falls back to the latest APPROVED report action when the report's status is APPROVED. A paid report has status REIMBURSED, so the function returns '' and the column is blank. The UI fallback transactionItem.approved ?? report?.approved doesn't help, because '' is not nullish.
Expense-report rows are not affected because they read the snapshot report.
This regression came from two PRs working together:
Not verified: I didn't confirm the OpenReport response shape. Comparing the report_<id> payload from OpenReport with the one from Search in a network trace would confirm it.
What changes do you think we should make in order to solve the problem?
In both transaction-row paths (getTransactionsSections and the transaction branch of getReportSections), make the missing date fall back to the snapshot:
When the live report has no approved, use the snapshot report's approved before calling getApprovedDate.
Apply the same fallback to submitted for getSubmittedDate, which has the same gap.
getApprovedDate already returns blank for Open and Submitted reports, so a stale snapshot date can't reappear after unapproving.
Add a unit test with a paid report: the live report has no approved, and the snapshot report has it. The Approved column should show the snapshot date.
What alternative solutions did you explore? (Optional)
You could also let getApprovedDate use the latest APPROVED action for REIMBURSED (and approved-then-closed) reports. Once a report is paid, the latest approval is the final one.
On its own this is less reliable:
OpenReport loads only the newest page of actions.
Live actions replace snapshot actions entirely, so the approval action may be missing on busy reports.
Use it only as an extra safeguard after the snapshot fallback.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/104768) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
changed the title [-]Approved date column is not populated for that expense that has a proper approved date.[/-][+][$175] Approved date column is not populated for that expense that has a proper approved date.[/+]on Oct 22, 2026
Please re-state the problem that we are trying to solve in this issue.
On Spend > Expenses, the optional Approved column is empty for an expense that was approved and has a valid approved date. It happens when the user clears the cache, opens the parent expense report from the Inbox first, and only then goes to the Spend page. The Approved date should show the same way it does when the Spend page is opened first.
What is the root cause of that problem?
Kimi's analysis was empty and the thread has no comments, so I could not confirm the cause against the source. This is my best reading of the repro, and the first step in the proposal is to verify it.
The approved date is only available in the search snapshot (SearchTransaction.approved), or in report actions that the snapshot or search response supplies. It is not a field on the normal transactions_ entry that OpenReport writes to Onyx.
Opening the report first puts a partial copy of the transaction, report and report actions into Onyx, from OpenReport/GetOlderActions. That copy has no approved value and may not contain the APPROVED action.
The Search list-building code (SearchUIUtils.getTransactionsSections / getTransactionItemCommonFormattedProperties) then appears to prefer or merge that live Onyx data over the snapshot data. The approved date is computed from data that does not have it, or is replaced by undefined. The column then renders empty.
With a clean cache and the Spend page opened first, only the snapshot data exists, so the column is populated. This matches the order dependence in the repro.
What changes do you think we should make in order to solve the problem?
Confirm the cause. After the repro, inspect snapshot_<hash>.data.transactions_<id>.approved and the local transactions_<id> in Onyx. If the snapshot has the date and the UI does not, the cause is the override described above.
Make the snapshot the source of truth for approved. In SearchUIUtils (transaction item creation), do not let local Onyx data overwrite snapshot-only fields:
// before: local data spread last, so it can clobber snapshot-only fieldsconsttransactionItem={
...snapshotTransaction,
...localTransaction,};// after: snapshot-only fields are resolved explicitlyconsttransactionItem={
...localTransaction,
...snapshotTransaction,approved: snapshotTransaction.approved??getApprovedDateFromActions(reportActions,reportID)??localTransaction?.approved,submitted: snapshotTransaction.submitted??localTransaction?.submitted,};
getApprovedDateFromActions is a small helper that returns the created date of the last CONST.REPORT.ACTIONS.TYPE.APPROVED action for the report. It is only a fallback for when the snapshot does not have the field.
Guard Onyx updates against clearing snapshot fields. If the cause is an Onyx update from OpenReport merging approved: null/undefined into the snapshot, drop those keys before merging (or skip them in the snapshot-update code path) so an existing value is never erased by a partial payload.
Make the column renderer tolerant. In the Approved column component (TransactionItemRow / DateCell), format item.approved and render an empty cell only when it is truly absent.
What alternative solutions did you explore? (Optional)
Force a Search refetch whenever the Spend page mounts. This hides the symptom but costs extra network calls and flickers. It also leaves the merge/precedence bug in place.
Make OpenReport return approved on the transaction. This needs a backend change and a new field on the Transaction type. It is a heavier fix and is not needed if the snapshot already carries the value.
In Spend > Expenses, when filtering by Paid status (or looking at an expense whose parent report was approved and reimbursed), the Approved column is empty if the user cleared their cache and opened the parent expense report from the Inbox before navigating to the Spend page.
Root Cause
In src/libs/SearchUIUtils.ts:
When building rows for the expenses table (getTransactionsSections and getReportActionsSections), SearchUIUtils computes the approved date via getApprovedDate(report, actions).
When the user visits the expense report from the Inbox first, OpenReport populates the live report in Onyx (reports_<id>).
3. The server payload from OpenReport does not populate report.approved (this is only provided in Search snapshot results).
4. getApprovedDate (lines 2723–2732):
Notice line 2730: actionApproved is strictly gated on reportItem.statusNum === CONST.REPORT.STATUS_NUM.APPROVED.
When a report is paid/reimbursed, reportItem.statusNum is CONST.REPORT.STATUS_NUM.REIMBURSED (or closed). Because reportItem.approved was not on the live report object from OpenReport and reportItem.statusNum !== CONST.REPORT.STATUS_NUM.APPROVED, actionApproved resolves to ''. Thus getApprovedDate returns '', leaving the Approved date column completely blank despite the report having a valid APPROVED report action.
If the live report in Onyx lacks approved, it should also fall back to the snapshot's report data[${ONYXKEYS.COLLECTION.REPORT}${transactionItem.reportID}]?.approved.
Solution
In src/libs/SearchUIUtils.ts:
In getApprovedDate: A report that reached CONST.REPORT.STATUS_NUM.REIMBURSED (or settled/closed) was already approved prior to reimbursement. Allow findActionByCreated to retrieve the APPROVED action timestamp when statusNum === CONST.REPORT.STATUS_NUM.APPROVED || statusNum === CONST.REPORT.STATUS_NUM.REIMBURSED.
Also allow fallback to snapshot report's approved timestamp if the live report does not have approved set.
Additionally, in getTransactionsSections and getReportActionsSections:
When retrieving the report object for property extraction, if the live report in Onyx does not carry approved, fall back to the snapshot report:
And pass effectiveReport to getApprovedDate(effectiveReport, actions).
Alternative Solutions Explored
Adding approved field to OpenReport response: Requires backend modifications and would still leave offline/historical local reports without the date. Computing from the APPROVED action and Search snapshot data is already the pattern used by the client for submitted and offline state synchronization.
this is what I found while reviewing #103956 but the problem seems to be from main. And as the reproduction is not straight-forward I chose to dig it myself and here is my finding.
Bug:Approved column shows blank for reports that were genuinely approved and the BE has properly populated the approved field in snapshot data.
Cause: the report object used to compute these columns prefers a global, app-wide live report cache over Search's own response data:
Since approved is only populated on Search's response but aren't reliably present on that live onyx key, any report already cached elsewhere (report opened in inbox before spend page) in the app silently loses Search's correct value. (edited)
Thanks, FitseTLT. Your finding matches the root cause in my proposal. Search prefers the live report, which lacks approved, over the snapshot report, which has it.
One detail explains why the column is blank and not just stale. getApprovedDate only falls back to the APPROVED report action when the report status is APPROVED. A paid report gets no fallback, so it returns ''.
The fix is the same either way. At both lines you linked, fill approved and submitted from the snapshot report when the live report doesn't have them. A broader option is to layer the live report over the snapshot report. That keeps every server-only field, but it may also keep stale snapshot values, so it needs more testing.
Please re-state the problem that we are trying to solve in this issue.
On the Spend > Expenses table, when filtering by status (such as Paid) and adding the "Approved" column, opening an expense report from Inbox before opening the Spend page causes the Approved date column for that expense to be blank/unpopulated, even though the expense was legitimately approved and paid.
What is the root cause of that problem?
There are two interconnected root causes in src/libs/SearchUIUtils.ts:
Live report preference drops server-computed snapshot metadata:
In getTransactionsSections (line 1878) and getReportActionsSections (line 2951), the code resolves the report using:
When the report has been opened in Inbox, OpenReport populates the live Onyx report report_<id>. However, OpenReport only returns standard report fields and does not include server-calculated search snapshot fields like approved or submitted. Because getReportOrDraftReport returns this live report, the fallback to data[${ONYXKEYS.COLLECTION.REPORT}${transactionItem.reportID}] is bypassed, and report.approved becomes undefined.
getApprovedDate restricts report action fallback strictly to APPROVED status:
In getApprovedDate (lines 2728-2733):
When a report has already been reimbursed/settled (statusNum === CONST.REPORT.STATUS_NUM.REIMBURSED), statusNum === CONST.REPORT.STATUS_NUM.APPROVED evaluates to false. If reportItem.approved was omitted or cleared by the live report, actionApproved evaluates to '', resulting in getApprovedDate returning ''.
What changes do you think we should make in order to solve the problem?
Merge live report with search snapshot metadata in SearchUIUtils.ts:
In both getTransactionsSections and getReportActionsSections, preserve server-computed snapshot fields (approved, submitted) when overlaying live Onyx updates:
Allow approved date fallback on reimbursed/settled reports in getApprovedDate:
In getApprovedDate (src/libs/SearchUIUtils.ts), allow reports that have reached terminal paid/reimbursed status to also reference their historical APPROVED report action when reportItem.approved is missing:
What alternative solutions did you explore? (Optional)
Modifying backend OpenReport API: Rejected because backend API contract changes take longer to deploy and do not repair existing cached client Onyx states.
Relying solely on APPROVED report action fallback without preserving snapshot: Rejected because Search queries with limit do not fetch full report action histories for all search items, making snapshot preservation essential.
@MelvinBot I think we should have a more wholistic solution not only specific fields can u investigate on why/when the live data was added with the snapshot as the fallback.
The live-first lookup was a deliberate review request, added on 2026-04-28 in Fall back to Onyx when search snapshot is missing report data. Its goal was to fix rows whose report was missing from a limited Search response. Nobody checked whether the live report has every field the snapshot has, and it doesn't. That gap causes this bug.
Holistic fix: add one helper in SearchUIUtils and use it at every live-first site. The helper layers the live report over the snapshot report:
Live values still win, so optimistic and offline edits keep showing.
Snapshot values fill fields the live report never gets (approved, submitted, paid, declined), with no per-field list to maintain.
This also covers point 2 of the original review: "Do this consistently for all data, not only specific fields."
Trade-off: if the server deletes a key from the live report (merges null), the stale snapshot value shows until the next Search refresh. That risk is smaller than today's, where every Search-only field is lost.
Timeline
2025-04:Add New Task Section to Reports started the pattern in getTasksSections. Onyx came first, with the snapshot as a fallback. The PR gives no reason.
2026-08:optimistically update some columns in offline built getApprovedDate, getSubmittedDate and getPaidDate on the live-first report. These read the report action only for APPROVED status, so a paid report shows a blank date.
⏱️ 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.
It implements the holistic fix. A new getSearchRowReport helper layers the live report over the Search snapshot report, so live values still win and Search-only fields like approved are kept.
Details
Sites switched to the helper:getTransactionsSections, the transaction branch of getReportSections, the task parent report in getTaskSections, and the custom-column check in getColumnsToShow.
Left unchanged:getReportActionsSections (chat rows), which doesn't render Search-only report fields. Switching it would need an unsafe type assertion that lint rejects. createAndOpenSearchTransactionThread is also unchanged, because it falls back to the row's own report, not the snapshot.
Tests: A new unit test covers a paid report whose live report lacks approved. It fails without the change and passes with it. All 86 Search test suites pass, and typecheck, lint-changed, and spell-changed pass.
Not done: I didn't run the manual repro in the app. The Tests and QA sections are left for you to fill in.
Activity
MelvinBot commentedon Oct 22, 2026
Proposal
What is the root cause of that problem?
On the Expenses view, Search builds each row from the live Onyx report when one exists, not from the Search snapshot report. Opening the report in Inbox first creates that live report.
The live report from
OpenReportdoesn't carryapproved. The App never sets that field itself; only the server sends it.getApprovedDateonly falls back to the latestAPPROVEDreport action when the report's status isAPPROVED. A paid report has statusREIMBURSED, so the function returns''and the column is blank. The UI fallbacktransactionItem.approved ?? report?.approveddoesn't help, because''is not nullish.Expense-report rows are not affected because they read the snapshot report.
This regression came from two PRs working together:
APPROVEDstatus.Code references
SearchUIUtils.ts:1875andSearchUIUtils.ts:2951SearchUIUtils.ts:1925andSearchUIUtils.ts:2999APPROVED:SearchUIUtils.ts:2723-2732SearchUIUtils.ts:2910TransactionItemRowWide.tsx:305Not verified: I didn't confirm the
OpenReportresponse shape. Comparing thereport_<id>payload fromOpenReportwith the one fromSearchin a network trace would confirm it.What changes do you think we should make in order to solve the problem?
In both transaction-row paths (
getTransactionsSectionsand the transaction branch ofgetReportSections), make the missing date fall back to the snapshot:approved, use the snapshot report'sapprovedbefore callinggetApprovedDate.submittedforgetSubmittedDate, which has the same gap.getApprovedDatealready returns blank for Open and Submitted reports, so a stale snapshot date can't reappear after unapproving.Add a unit test with a paid report: the live report has no
approved, and the snapshot report has it. The Approved column should show the snapshot date.What alternative solutions did you explore? (Optional)
You could also let
getApprovedDateuse the latestAPPROVEDaction forREIMBURSED(and approved-then-closed) reports. Once a report is paid, the latest approval is the final one.On its own this is less reliable:
OpenReportloads only the newest page of actions.Use it only as an extra safeguard after the snapshot fallback.
Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/104768)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
melvin-bot commentedon Oct 22, 2026
Current assignee @FitseTLT is eligible for the External assigner, not assigning anyone new.
[-]Approved date column is not populated for that expense that has a proper approved date.[/-][+][$175] Approved date column is not populated for that expense that has a proper approved date.[/+]Abdallah-1551 commentedon Oct 22, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
On Spend > Expenses, the optional Approved column is empty for an expense that was approved and has a valid approved date. It happens when the user clears the cache, opens the parent expense report from the Inbox first, and only then goes to the Spend page. The Approved date should show the same way it does when the Spend page is opened first.
What is the root cause of that problem?
Kimi's analysis was empty and the thread has no comments, so I could not confirm the cause against the source. This is my best reading of the repro, and the first step in the proposal is to verify it.
SearchTransaction.approved), or in report actions that the snapshot or search response supplies. It is not a field on the normaltransactions_entry thatOpenReportwrites to Onyx.OpenReport/GetOlderActions. That copy has noapprovedvalue and may not contain theAPPROVEDaction.SearchUIUtils.getTransactionsSections/getTransactionItemCommonFormattedProperties) then appears to prefer or merge that live Onyx data over the snapshot data. The approved date is computed from data that does not have it, or is replaced byundefined. The column then renders empty.What changes do you think we should make in order to solve the problem?
Confirm the cause. After the repro, inspect
snapshot_<hash>.data.transactions_<id>.approvedand the localtransactions_<id>in Onyx. If the snapshot has the date and the UI does not, the cause is the override described above.Make the snapshot the source of truth for
approved. InSearchUIUtils(transaction item creation), do not let local Onyx data overwrite snapshot-only fields:getApprovedDateFromActionsis a small helper that returns thecreateddate of the lastCONST.REPORT.ACTIONS.TYPE.APPROVEDaction for the report. It is only a fallback for when the snapshot does not have the field.Guard Onyx updates against clearing snapshot fields. If the cause is an Onyx update from
OpenReportmergingapproved: null/undefinedinto the snapshot, drop those keys before merging (or skip them in the snapshot-update code path) so an existing value is never erased by a partial payload.Make the column renderer tolerant. In the Approved column component (
TransactionItemRow/DateCell), formatitem.approvedand render an empty cell only when it is truly absent.What alternative solutions did you explore? (Optional)
OpenReportreturnapprovedon the transaction. This needs a backend change and a new field on theTransactiontype. It is a heavier fix and is not needed if the snapshot already carries the value.melvin-bot commentedon Oct 22, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022113304712695120110
FitseTLT commentedon Oct 22, 2026
@Abdallah-1551 donot post proposals before Help Wanted label is applied
FitseTLT commentedon Oct 22, 2026
@MelvinBot can u access the slack thread this bug was reported if not I will post you here the discussion we had.
camesenin commentedon Oct 22, 2026
Proposal
Problem
In Spend > Expenses, when filtering by
Paidstatus (or looking at an expense whose parent report was approved and reimbursed), the Approved column is empty if the user cleared their cache and opened the parent expense report from the Inbox before navigating to the Spend page.Root Cause
In
src/libs/SearchUIUtils.ts:getTransactionsSectionsandgetReportActionsSections),SearchUIUtilscomputes the approved date viagetApprovedDate(report, actions).When the user visits the expense report from the Inbox first,
OpenReportpopulates the livereportin Onyx (reports_<id>).3. The server payload from
OpenReportdoes not populatereport.approved(this is only provided in Search snapshot results).4.
getApprovedDate(lines 2723–2732):actionApprovedis strictly gated onreportItem.statusNum === CONST.REPORT.STATUS_NUM.APPROVED.When a report is paid/reimbursed,
reportItem.statusNumisCONST.REPORT.STATUS_NUM.REIMBURSED(or closed). BecausereportItem.approvedwas not on the livereportobject fromOpenReportandreportItem.statusNum !== CONST.REPORT.STATUS_NUM.APPROVED,actionApprovedresolves to''. ThusgetApprovedDatereturns'', leaving the Approved date column completely blank despite the report having a validAPPROVEDreport action.Furthermore, in
getTransactionsSections:If the live report in Onyx lacks
approved, it should also fall back to the snapshot's reportdata[${ONYXKEYS.COLLECTION.REPORT}${transactionItem.reportID}]?.approved.Solution
src/libs/SearchUIUtils.ts:getApprovedDate: A report that reachedCONST.REPORT.STATUS_NUM.REIMBURSED(or settled/closed) was already approved prior to reimbursement. AllowfindActionByCreatedto retrieve theAPPROVEDaction timestamp whenstatusNum === CONST.REPORT.STATUS_NUM.APPROVED || statusNum === CONST.REPORT.STATUS_NUM.REIMBURSED.approvedtimestamp if the live report does not haveapprovedset.getTransactionsSectionsandgetReportActionsSections:When retrieving the report object for property extraction, if the live report in Onyx does not carry
approved, fall back to the snapshot report:And pass
effectiveReporttogetApprovedDate(effectiveReport, actions).Alternative Solutions Explored
approvedfield toOpenReportresponse: Requires backend modifications and would still leave offline/historical local reports without the date. Computing from theAPPROVEDaction and Search snapshot data is already the pattern used by the client forsubmittedand offline state synchronization.MelvinBot commentedon Oct 22, 2026
No, I can't read that Slack thread from here. Please post the discussion in this issue.
I'll compare it with my proposal, which says the live report from
OpenReporthas noapproveddate. These details would help most:OpenReportresponse includesapproved.FitseTLT commentedon Oct 22, 2026
@MelvinBot
this is what I found while reviewing #103956 but the problem seems to be from main. And as the reproduction is not straight-forward I chose to dig it myself and here is my finding.
Bug:Approved column shows blank for reports that were genuinely approved and the BE has properly populated the approved field in snapshot data.
Cause: the report object used to compute these columns prefers a global, app-wide live report cache over Search's own response data:
getReportOrDraftReport(id) ?? data[report_id]
App/src/libs/SearchUIUtils.ts
Line 1873 in 9a6e664
App/src/libs/SearchUIUtils.ts
Line 2949 in 9a6e664
Since
approvedis only populated on Search's response but aren't reliably present on that live onyx key, any report already cached elsewhere (report opened in inbox before spend page) in the app silently loses Search's correct value. (edited)MelvinBot commentedon Oct 22, 2026
Thanks,
FitseTLT. Your finding matches the root cause in my proposal. Search prefers the live report, which lacksapproved, over the snapshot report, which has it.One detail explains why the column is blank and not just stale.
getApprovedDateonly falls back to theAPPROVEDreport action when the report status isAPPROVED. A paid report gets no fallback, so it returns''.The fix is the same either way. At both lines you linked, fill
approvedandsubmittedfrom the snapshot report when the live report doesn't have them. A broader option is to layer the live report over the snapshot report. That keeps every server-only field, but it may also keep stale snapshot values, so it needs more testing.Code references
SearchUIUtils.ts:1875andSearchUIUtils.ts:2951APPROVEDstatus:SearchUIUtils.ts:2723-2732melvin-bot commentedon Oct 22, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
Anthgg commentedon Oct 22, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
On the Spend > Expenses table, when filtering by status (such as Paid) and adding the "Approved" column, opening an expense report from Inbox before opening the Spend page causes the Approved date column for that expense to be blank/unpopulated, even though the expense was legitimately approved and paid.
What is the root cause of that problem?
There are two interconnected root causes in
src/libs/SearchUIUtils.ts:Live report preference drops server-computed snapshot metadata:
In
getTransactionsSections(line 1878) andgetReportActionsSections(line 2951), the code resolves the report using:When the report has been opened in Inbox,
OpenReportpopulates the live Onyx reportreport_<id>. However,OpenReportonly returns standard report fields and does not include server-calculated search snapshot fields likeapprovedorsubmitted. BecausegetReportOrDraftReportreturns this live report, the fallback todata[${ONYXKEYS.COLLECTION.REPORT}${transactionItem.reportID}]is bypassed, andreport.approvedbecomesundefined.getApprovedDaterestricts report action fallback strictly toAPPROVEDstatus:In
getApprovedDate(lines 2728-2733):When a report has already been reimbursed/settled (
statusNum === CONST.REPORT.STATUS_NUM.REIMBURSED),statusNum === CONST.REPORT.STATUS_NUM.APPROVEDevaluates tofalse. IfreportItem.approvedwas omitted or cleared by the live report,actionApprovedevaluates to'', resulting ingetApprovedDatereturning''.What changes do you think we should make in order to solve the problem?
Merge live report with search snapshot metadata in
SearchUIUtils.ts:In both
getTransactionsSectionsandgetReportActionsSections, preserve server-computed snapshot fields (approved,submitted) when overlaying live Onyx updates:Allow approved date fallback on reimbursed/settled reports in
getApprovedDate:In
getApprovedDate(src/libs/SearchUIUtils.ts), allow reports that have reached terminal paid/reimbursed status to also reference their historicalAPPROVEDreport action whenreportItem.approvedis missing:What alternative solutions did you explore? (Optional)
OpenReportAPI: Rejected because backend API contract changes take longer to deploy and do not repair existing cached client Onyx states.APPROVEDreport action fallback without preserving snapshot: Rejected because Search queries withlimitdo not fetch full report action histories for all search items, making snapshot preservation essential.Contributor details:
GitHub: @Anthgg
FitseTLT commentedon Oct 27, 2026
Reviewing tomorrow
FitseTLT commentedon Oct 29, 2026
@MelvinBot I think we should have a more wholistic solution not only specific fields can u investigate on why/when the live data was added with the snapshot as the fallback.
MelvinBot commentedon Oct 29, 2026
The live-first lookup was a deliberate review request, added on 2026-04-28 in Fall back to Onyx when search snapshot is missing report data. Its goal was to fix rows whose report was missing from a limited Search response. Nobody checked whether the live report has every field the snapshot has, and it doesn't. That gap causes this bug.
Holistic fix: add one helper in
SearchUIUtilsand use it at every live-first site. The helper layers the live report over the snapshot report:approved,submitted,paid,declined), with no per-field list to maintain.Trade-off: if the server deletes a key from the live report (merges
null), the stale snapshot value shows until the next Search refresh. That risk is smaller than today's, where every Search-only field is lost.Timeline
getTasksSections. Onyx came first, with the snapshot as a fallback. The PR gives no reason.getReportActionsSectionsfrom snapshot-only to live-first so offline task edits appear in Search.luacmartinsthen requested live-first "like we do for todos", and commita70696531e0flipped all three sites before merge.getApprovedDate,getSubmittedDateandgetPaidDateon the live-first report. These read the report action only forAPPROVEDstatus, so a paid report shows a blank date.Inconsistency: Resolve reports snapshot-first in Search bulk Delete and Reject went the other way. Bulk actions use the snapshot first (
Search.ts:173-181), while row-building uses the live report first.Sites to switch to the helper
SearchUIUtils.ts:1938:getTransactionsSections. This is where the bug shows.SearchUIUtils.ts:3100: the transaction branch ofgetReportSections. This is where the bug shows.SearchUIUtils.ts:2637:createAndOpenSearchTransactionThread.SearchUIUtils.ts:2696-2697:getReportActionsSections.SearchUIUtils.ts:2494: the task parent report.SearchUIUtils.ts:7176: custom columns.Leave
SearchUIUtils.ts:2625live-only. It deliberately avoids a stale snapshot thread that would create a duplicate IOU action.Not verified: I didn't capture an
OpenReportpayload. The claim that it omitsapproved,submittedandpaidcomes from this issue and [Due for payment 2026-08-25] Spend - First approver field does not show the approver after approving report offline.Next Steps: Reply with
@MelvinBot implement thisto create a draft PR, or@MelvinBot <your feedback>to refine this analysis.view run
duongynhi000005-oss commentedon Nov 1, 2026
FitseTLT commentedon Nov 2, 2026
@MelvinBot implement
MelvinBot commentedon Nov 2, 2026
⏱️ 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
FitseTLT commentedon Nov 3, 2026
@MelvinBot check #104768 (comment) again
MelvinBot commentedon Nov 3, 2026
🤖 Draft PR created: Keep Search-only report fields when the live report is already in Onyx
Please mark it as "Ready for review" or ask me to do that for you when it is ready for review.
It implements the holistic fix. A new
getSearchRowReporthelper layers the live report over the Search snapshot report, so live values still win and Search-only fields likeapprovedare kept.Details
getTransactionsSections, the transaction branch ofgetReportSections, the task parent report ingetTaskSections, and the custom-column check ingetColumnsToShow.getReportActionsSections(chat rows), which doesn't render Search-only report fields. Switching it would need an unsafe type assertion that lint rejects.createAndOpenSearchTransactionThreadis also unchanged, because it falls back to the row's own report, not the snapshot.approved. It fails without the change and passes with it. All 86 Search test suites pass, and typecheck,lint-changed, andspell-changedpass.TestsandQAsections are left for you to fill in.view run