Wrong system message after remove expense from report
If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!
Version Number: v9.2.75-0 Reproducible in staging?: Yes Reproducible in production?: Yes If this was caught during regression testing, add the test name, ID and link from BrowserStack: https://github.com/Expensify/App/pull/76209 Issue reported by: Applause Internal Team Bug source: Pull Request QA execution Device used: MacOS Monterey 12.7.4 / Chrome App Component: Other
Action Performed:
- Go to workspace chat.
- Create an expense.
- Open the transaction thread.
- Click Report.
- Click Remove from report.
- Go to self DM.
- Open the unreported expense.
Expected Result:
System message "Moved this expense to your personal space" shown
Actual Result:
System message "Moved this expense from
Workaround:
Unknown
Platforms:
- [x] Android: App
- [ ] Android: mWeb Chrome
- [x] iOS: App
- [ ] iOS: mWeb Safari
- [ ] iOS: mWeb Chrome
- [x] Windows: Chrome
- [ ] MacOS: Chrome / Safari
- [ ] MacOS: Desktop
Screenshots/Videos
https://github.com/user-attachments/assets/cb91b859-a2d8-4f38-8e66-37feb5bb0c2b
Triggered auto assignment to @muttmuure (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.
Proposal
Please re-state the problem that we are trying to solve in this issue.
When removing an expense from a report, the system message shows "Moved this expense from " (incomplete) instead of "Moved this expense to your personal space".
What is the root cause of that problem?
The issue is in the getUnreportedTransactionMessage function in App/src/libs/ReportUtils.ts (around line 7091).
Current problematic logic:
function getUnreportedTransactionMessage(action: ReportAction) {
const movedTransactionOriginalMessage = getOriginalMessage(action) ?? {};
const {fromReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction;
const fromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`];
// eslint-disable-next-line @typescript-eslint/no-deprecated
const reportName = getReportName(fromReport) ?? fromReport?.reportName ?? '';
let reportUrl = getReportURLForCurrentContext(fromReportID);
if (fromReportID === CONST.REPORT.UNREPORTED_REPORT_ID) {
reportUrl = `${environmentURL}/r/${findSelfDMReportID()}`;
// eslint-disable-next-line @typescript-eslint/no-deprecated
return translateLocal('iou.unreportedTransaction', {
reportUrl,
});
}
// eslint-disable-next-line @typescript-eslint/no-deprecated
return translateLocal('iou.movedTransactionFrom', {
reportUrl,
reportName,
});
}
Why this happens:
- When an expense is removed from a report, an
UNREPORTED_TRANSACTIONaction is created viabuildOptimisticUnreportedTransactionActionwithfromReportIDset to the report the expense was removed from - When displaying the action,
getUnreportedTransactionMessageis called - The function checks if
fromReportID === CONST.REPORT.UNREPORTED_REPORT_ID(line 7102) - Since the expense is being removed FROM a report (not FROM personal space), this condition is
false - The function then tries to retrieve the report name from
allReportsusingfromReportID - If the report doesn't exist in
allReportsorgetReportNamereturns an empty value,reportNamebecomes an empty string - The function calls
translateLocal('iou.movedTransactionFrom', {reportUrl, reportName})with an emptyreportName - The translation
movedTransactionFromreturns "moved this expense from " (incomplete) because it expects a non-emptyreportName
The fundamental flaw:
The UNREPORTED_TRANSACTION action type indicates that an expense was moved TO personal space (unreported), not FROM somewhere. The function incorrectly tries to show "moved from {reportName}" when fromReportID is not UNREPORTED_REPORT_ID. The function should always show "moved this expense to your personal space" for UNREPORTED_TRANSACTION actions, because the action type itself inherently represents moving TO personal space.
What changes do you think we should make in order to solve the problem?
Simplify the getUnreportedTransactionMessage function to always return the "moved to personal space" message, since UNREPORTED_TRANSACTION actions inherently mean the expense was moved to personal space.
File: App/src/libs/ReportUtils.ts
Location: getUnreportedTransactionMessage function (around line 7091)
Change the function from:
function getUnreportedTransactionMessage(action: ReportAction) {
const movedTransactionOriginalMessage = getOriginalMessage(action) ?? {};
const {fromReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction;
const fromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`];
// eslint-disable-next-line @typescript-eslint/no-deprecated
const reportName = getReportName(fromReport) ?? fromReport?.reportName ?? '';
let reportUrl = getReportURLForCurrentContext(fromReportID);
if (fromReportID === CONST.REPORT.UNREPORTED_REPORT_ID) {
reportUrl = `${environmentURL}/r/${findSelfDMReportID()}`;
// eslint-disable-next-line @typescript-eslint/no-deprecated
return translateLocal('iou.unreportedTransaction', {
reportUrl,
});
}
// eslint-disable-next-line @typescript-eslint/no-deprecated
return translateLocal('iou.movedTransactionFrom', {
reportUrl,
reportName,
});
}
To:
function getUnreportedTransactionMessage(action: ReportAction) {
const reportUrl = `${environmentURL}/r/${findSelfDMReportID()}`;
// eslint-disable-next-line @typescript-eslint/no-deprecated
return translateLocal('iou.unreportedTransaction', {
reportUrl,
});
}
Why this works:
- Simplified logic: Since
UNREPORTED_TRANSACTIONalways means the expense was moved TO personal space, we don't need to check thefromReportID - Consistent message: Always shows "moved this expense to your personal space" which accurately describes what happened
- No dependency on report existence: Removes the dependency on
fromReportexisting inallReports, which was causing the incomplete message - Matches action semantics: The action type
UNREPORTED_TRANSACTIONis specifically for moving expenses to personal space, so the message should reflect that destination
Translation used: iou.unreportedTransaction renders as: "moved this expense to your personal space" (with a link to the self DM)
What alternative solutions did you explore? (Optional)
None
Demo
@muttmuure Huh... This is 4 days overdue. Who can take care of this?
@muttmuure Still overdue 6 days?! Let's take care of this!
@muttmuure 10 days overdue. Is anyone even seeing these? Hello?
@muttmuure 12 days overdue now... This issue's end is nigh!
@muttmuure this issue was created 2 weeks ago. Are we close to a solution? Let's make sure we're treating this as a top priority. Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!
This issue has not been updated in over 14 days. @muttmuure eroding to Weekly issue.