Skip to content

[Due for payment 2025-06-23] [$250] Incorrect report action in the LHN #62323

Closed

Description

@m-natarajan

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.1.45-21
Reproducible in staging?: Y
Reproducible in production?: Y
If this was caught during regression testing, add the test name, ID and link from TestRail:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Expensify/Expensify Issue URL:
Issue reported by: @MonilBhavsar
Slack conversation (hyperlinked to channel name): #Expensify Bugs

Action Performed:

  1. Create n workspace and set submission to Manual
  2. Invite an employee to the workspace
  3. As an employee, create a report and add an expense to it
  4. Click to open the report details
  5. Observe the last message in Report chat.

Expected Result:

LHN should show “no activity.”

Actual Result:

It says "submitted", but report is still in "Open" state

Workaround:

Can the user still use Expensify without this being fixed? Have you informed them of the workaround?

Platforms:

Select the officially supported platforms where the issue was reproduced:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari
  • MacOS: Desktop
Platforms Tested: On which of our officially supported platforms was this issue tested:
  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari
  • MacOS: Desktop

Screenshots/Videos

Add any screenshot/video evidence
Screen.Recording.2025-05-16.at.7.25.05.PM.mov

Image

Image

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021924596788231412189
  • Upwork Job ID: 1924596788231412189
  • Last Price Increase: 2025-05-26
Issue OwnerCurrent Issue Owner: @mananjadhav

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on May 19, 2025
  2. melvin-bot commented on May 19, 2025

    @melvin-bot

    Triggered auto assignment to @twisterdotcom (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

  3. ganzz4 commented on May 19, 2025

    @ganzz4
    Contributor

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    When a report is in the "Open" state (not yet submitted), but contains an IOU (expense) action or a SUBMITTED action, the Left Hand Navigation (LHN) incorrectly displays a "submitted ..." message. Instead, it should display "no activity yet" until the report is actually submitted.

    What is the root cause of that problem?

    The function getLastMessageTextForReport, which generates the preview message in the Left Hand Navigation (LHN), determines the message based only on the last action type (e.g., IOU or SUBMITTED), without checking the report’s actual state.

    As a result, it can display a misleading "submitted ..." message even if the report is still in the "Open" state and hasn't been submitted. This logic appears in two places:

    • IOU (expense) actions
      } else if (isMoneyRequestAction(lastReportAction)) {
      const properSchemaForMoneyRequestMessage = getReportPreviewMessage(report, lastReportAction, true, false, null, true);
      lastMessageTextFromReport = formatReportLastMessageText(properSchemaForMoneyRequestMessage);
    • SUBMITTED actions
      } else if (
      isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) ||
      isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) ||
      isMarkAsClosedAction(lastReportAction)
      ) {
      const wasSubmittedViaHarvesting = !isMarkAsClosedAction(lastReportAction) ? getOriginalMessage(lastReportAction)?.harvesting ?? false : false;
      if (wasSubmittedViaHarvesting) {
      lastMessageTextFromReport = getReportAutomaticallySubmittedMessage(lastReportAction);
      } else {
      lastMessageTextFromReport = getIOUSubmittedMessage(lastReportAction);
      }

    To fix this, the function should verify the report’s state and only display "submitted ..." if it’s truly submitted. Otherwise, it should show "no activity yet" to reflect the correct status.

    What changes do you think we should make in order to solve the problem?

    Generalized fix:
    In both code sections above, add a check for the report's state.
    If the report is still open (stateNum === CONST.REPORT.STATE_NUM.OPEN), display "no activity yet" instead of "submitted ...".

    Example fix (applies to both places):

    if (report?.stateNum === CONST.REPORT.STATE_NUM.OPEN) {
        lastMessageTextFromReport = translateLocal('report.noActivityYet');
    } else {
        // existing logic for showing 'submitted ...'
    }

    By adding a single state check in both relevant branches of getLastMessageTextForReport, we ensure the LHN always reflects the true report status.

    What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?

    • Open report with only IOU action: LHN should display "no activity yet".
    • Open report with SUBMITTED action (but not actually submitted): LHN should display "no activity yet".
    • Report that is actually submitted: LHN should display "submitted ..." as before.

    What alternative solutions did you explore? (Optional)

    N/A

  4. ganzz4 commented on May 19, 2025

    @ganzz4
    Contributor

    I've recorded a quick demo video showing the fix in action:

    Result.mp4
  5. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on May 19, 2025
  6. melvin-bot commented on May 19, 2025

    @melvin-bot
  7. changed the title [-]Incorrect report action in the LHN[/-] [+][$250] Incorrect report action in the LHN[/+] on May 19, 2025
  8. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on May 19, 2025
  9. melvin-bot commented on May 19, 2025

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @abdulrahuman5196 (External)

  10. dmkt9 commented on May 19, 2025

    @dmkt9
    Contributor

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    It says "submitted", but report is still in "Open" state

    What is the root cause of that problem?

    • The text preview for LHN report chat is retrieved from the following functions and passed into OptionRowLHNData:

      1. Function getLastMessageTextForReport:
        const lastMessageTextFromReport = getLastMessageTextForReport(item, lastActorDetails, itemPolicy, itemReportNameValuePairs);
      2. Function getReportPreviewMessage:
        const properSchemaForMoneyRequestMessage = getReportPreviewMessage(report, lastReportAction, true, false, null, true);
    • And in the function getReportPreviewMessage, the returned value comes from the following logic:

      App/src/libs/ReportUtils.ts

      Lines 4297 to 4304 in e65db2e

      if (amount !== undefined && lastActorID && !isPreviewMessageForParentChatReport) {
      const amountToDisplay = convertToDisplayString(Math.abs(amount), currency);
      // We only want to show the actor name in the preview if it's not the current user who took the action
      const requestorName =
      lastActorID && lastActorID !== currentUserAccountID ? getDisplayNameForParticipant({accountID: lastActorID, shouldUseShortForm: !isPreviewMessageForParentChatReport}) : '';
      return `${requestorName ? `${requestorName}: ` : ''}${translateLocal('iou.submittedAmount', {formattedAmount: amountToDisplay, comment})}`;
      }

    • This logic does not check whether the report is in manual submit mode.

    What changes do you think we should make in order to solve the problem?

    • Add a check for pending Submit action using the isSubmitAction function at this point:
      if (amount !== undefined && lastActorID && !isPreviewMessageForParentChatReport) {

      function isSubmitAction(report: Report, reportTransactions: Transaction[], policy?: Policy, reportNameValuePairs?: ReportNameValuePairs) {
    if (amount !== undefined && lastActorID && !isPreviewMessageForParentChatReport) {
        if(isSubmitAction(report, allReportTransactions, policy ?? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`], allReportNameValuePair?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`])){
            return translateLocal('report.noActivityYet');
        }
    
    ...
    }

    What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?

    N/A

    What alternative solutions did you explore? (Optional)

    N/A

    Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.

  11. bernhardoj commented on May 19, 2025

    @bernhardoj
    Contributor

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    LHN last message shows submitted message when the report is still open.

    What is the root cause of that problem?

    The message is coming from here.

    } else if (isMoneyRequestAction(lastReportAction)) {
    const properSchemaForMoneyRequestMessage = getReportPreviewMessage(report, lastReportAction, true, false, null, true);
    lastMessageTextFromReport = formatReportLastMessageText(properSchemaForMoneyRequestMessage);

    App/src/libs/ReportUtils.ts

    Lines 4296 to 4303 in a95171a

    if (amount !== undefined && lastActorID && !isPreviewMessageForParentChatReport) {
    const amountToDisplay = convertToDisplayString(Math.abs(amount), currency);
    // We only want to show the actor name in the preview if it's not the current user who took the action
    const requestorName =
    lastActorID && lastActorID !== currentUserAccountID ? getDisplayNameForParticipant({accountID: lastActorID, shouldUseShortForm: !isPreviewMessageForParentChatReport}) : '';
    return `${requestorName ? `${requestorName}: ` : ''}${translateLocal('iou.submittedAmount', {formattedAmount: amountToDisplay, comment})}`;
    }

    It gets the last action (which is the IOU) and then gets the message from getReportPreviewMessage. Before #39483, the message would be requested $x.x for {comment}. Before #48139, the last action was empty, and "No activity yet" was shown.

    The last action is calculated here. For a one-transaction report, the report actions are combined, just like we do on the report screen, to filter out the IOU action. However, #48139 changes it so that no IOU actions are filtered, so basically, the combine logic does nothing.

    const transactionThreadReportID = getOneTransactionThreadReportID(reportID, actions[reportActions[0]]);
    if (transactionThreadReportID) {
    const transactionThreadReportActionsArray = Object.values(actions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`] ?? {});
    sortedReportActions = getCombinedReportActions(sortedReportActions, transactionThreadReportID, transactionThreadReportActionsArray, reportID, false);
    }
    const firstReportAction = sortedReportActions.at(0);
    if (!firstReportAction) {
    delete lastReportActions[reportID];
    } else {
    lastReportActions[reportID] = firstReportAction;
    }

    It's to fix #47843, where the 0.0 amount is shown as the last message while the receipt is scanning, because when the last action is empty, it falls back to the lastMessageText first.

    What changes do you think we should make in order to solve the problem?

    If we want to show "No activity yet" for one-transaction report, then we should filter out the IOU action by removing the last param.

    sortedReportActions = getCombinedReportActions(sortedReportActions, transactionThreadReportID, transactionThreadReportActionsArray, reportID, false);

    To handle 0.0 amount is shown as the last message, we shouldn't set the last message optimistically since BE doesn't set either.

    App/src/libs/actions/IOU.ts

    Lines 1335 to 1336 in a95171a

    lastMessageText: getReportActionText(iou.action),
    lastMessageHtml: getReportActionHtml(iou.action),

    Last, we need to change the ?? here to ||. For the expense report, the welcome message is an empty string, so the "No activity yet" fallback is never used. If we don't update this, the formattedLogin will be shown. If we update this, then we can remove the formattedLogin fallback.

    if (!lastMessageText) {
    lastMessageText = formatReportLastMessageText(getWelcomeMessage(report, policy).messageText ?? translateLocal('report.noActivityYet'));
    }
    if (shouldShowLastActorDisplayName(report, lastActorDetails, lastAction) && !isArchivedReport(reportNameValuePairs)) {
    result.alternateText = `${lastActorDisplayName}: ${formatReportLastMessageText(Parser.htmlToText(lastMessageText)) || formattedLogin}`;
    } else {
    result.alternateText = formatReportLastMessageText(Parser.htmlToText(lastMessageText)) || formattedLogin;
    }

    The above solution will handle for one-transaction report. If we create 2 money requests, then the last message will still show submitted $x.x for {comment} since the last action is IOU. If we want to use the old message (requested $x.x for {comment}), then we need to re-add the translation back and use it here.

    return `${requestorName ? `${requestorName}: ` : ''}${translateLocal('iou.submittedAmount', {formattedAmount: amountToDisplay, comment})}`;

    Image

    What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?

    We can test SidebarUtils.getOptionData and assert the alternateText is "No activity yet" for empty one-transaction report.

  12. melvin-bot commented on May 23, 2025

    @melvin-bot

    @abdulrahuman5196 Whoops! This issue is 2 days overdue. Let's get this updated quick!

  13. 36 remaining items

  14. melvin-bot commented on Jun 16, 2025

    @melvin-bot

    The solution for this issue has been 🚀 deployed to production 🚀 in version 9.1.66-5 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

    If no regressions arise, payment will be issued on 2025-06-23. 🎊

    For reference, here are some details about the assignees on this issue:

    • @mananjadhav requires payment through NewDot Manual Requests
    • @bernhardoj requires payment through NewDot Manual Requests
  15. melvin-bot commented on Jun 16, 2025

    @melvin-bot

    @mananjadhav @twisterdotcom @mananjadhav The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]

  16. melvin-bot commented on Jun 23, 2025

    @melvin-bot

    Payment Summary

    Upwork Job

    BugZero Checklist (@twisterdotcom)

    • I have verified the correct assignees and roles are listed above and updated the necessary manual offers
    • I have verified that there are no duplicate or incorrect contracts on Upwork for this job (https://www.upwork.com/ab/applicants/1924596788231412189/hired)
    • I have paid out the Upwork contracts or cancelled the ones that are incorrect
    • I have verified the payment summary above is correct
  17. twisterdotcom commented on Jun 23, 2025

    @twisterdotcom
    Contributor

    Payment Summary:

  18. twisterdotcom commented on Jun 23, 2025

    @twisterdotcom
    Contributor

    Could we get a checklist here please @mananjadhav?

  19. mananjadhav commented on Jun 23, 2025

    @mananjadhav
    Collaborator

    BugZero Checklist:

    • [Contributor] Classify the bug:
    Bug classification

    Source of bug:

    • 1a. Result of the original design (eg. a case wasn't considered)
    • 1b. Mistake during implementation
    • 1c. Backend bug
    • 1z. Other:

    Where bug was reported:

    • 2a. Reported on production (eg. bug slipped through the normal regression and PR testing process on staging)
    • 2b. Reported on staging (eg. found during regression or PR testing)
    • 2d. Reported on a PR
    • 2z. Other:

    Who reported the bug:

    • 3a. Expensify user
    • 3b. Expensify employee
    • 3c. Contributor
    • 3d. QA
    • 3z. Other:

    Regression Test Proposal

    Precondition:

    • Have a workspace chat with the no unsettled expense

    Test:

    1. Open a workspace chat with no unsettled expense
    2. Create a new request
    3. Open the expense report (or pin it on mobile)
    4. Verify the expense report LHN item shows "No activity yet".

    Do we agree 👍 or 👎

  20. mananjadhav commented on Jun 23, 2025

    @mananjadhav
    Collaborator
  21. bernhardoj commented on Jun 23, 2025

    @bernhardoj
    Contributor

    Requested in ND.

  22. garrettmknight commented on Jun 27, 2025

    @garrettmknight
    Contributor

    $250 approved for @mananjadhav

  23. JmillsExpensify commented on Jul 2, 2025

    @JmillsExpensify
    Contributor

    $250 approved for @bernhardoj

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionBugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributor

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions