Skip to content

feat: Reject Expense in NewDot#62787

Merged
MariaHCD merged 227 commits intoExpensify:mainfrom
mananjadhav:feat/52049-mj-decline-expense
Sep 12, 2025
Merged

feat: Reject Expense in NewDot#62787
MariaHCD merged 227 commits intoExpensify:mainfrom
mananjadhav:feat/52049-mj-decline-expense

Conversation

@mananjadhav
Copy link
Collaborator

@mananjadhav mananjadhav commented May 26, 2025

Explanation of Change

  • This PR covers the implementation of the Reject Expense (previously Decline Expense ) feature. It involves allowing approvers to decline reject the expenses and the submitter can then resolve the rejected expenses.
  • It also involves adding/resolving the transaction violations, which are then used to check the reject message status.
  • The Reject action is also available for IOU chats, without the Mark as resolve action.

Fixed Issues

$ #52049
PROPOSAL:

Tests

A. Hold vs Reject Modal
Precondition:

  • Logged in as an approver or workspace admin.
  • An expense is present in a report and can be rejected.
  • NVP nvp_dismissedRejectUseExplanation is not set in the account.

Test:

  1. Open the expense details view.
  2. Click on the More menu and select Reject.
  3. Verify that the Hold vs Reject modal is opened.
  4. Click Got it in the modal.
  5. The modal should close and the reject reason page should open.
  6. Verify nvp_dismissedRejectUseExplanation set to true.
  7. Exit out of the report and repeat the steps from 1 again.
  8. This time the modal shouldn't open and the reject reason page should open directly. All future rejections should skip the modal.

B. Delayed Submission Workspace – Multiple expenses – Existing draft report available
Precondition:

  • Logged in as an approver or workspace admin.
  • Workspace has Delayed submission enabled
  • Current report has multiple expenses.
  • Another draft report exists in the same workspace by the submitter

Test:

  1. Open a report with multiple expenses.
  2. Click on the More menu for one expense and select Reject.
  3. Enter a reject reason and confirm.
  4. Verify the expense is removed from the original report.
  5. Verify the expense is moved to the existing draft report.
  6. Verify the original report total is updated.
  7. Verify system messages are added to both the original report and the transaction thread.
    a. rejected this expense is the first message added to the report.
    b. rejected reason as a comment is the next message

C. Delayed Submission Workspace – Multiple expenses – No existing draft report
Precondition:

  • Logged in as an approver or workspace admin.
  • Workspace Scheduled Submit enabled, frequency != Instant.
  • Current report has multiple expenses.
  • No draft report exists in the workspace.

Test:

  1. Open a report with multiple expenses.
  2. Click on the More menu for one expense and select Reject.
  3. Enter a reject reason and confirm.
  4. Verify the expense is removed from the original report.
  5. Verify a new draft report is created and the expense is moved there.
  6. Verify the original report total is updated.
  7. Verify system messages are added to both the original report and the transaction thread. They're the same as the cases in test B.

D. Instant Submission Workspace – Single expense and multiple expenses
Precondition:

  • Logged in as an approver or workspace admin.
  • Workspace Delayed submission is disabled.

Test 1: Multiple expenses

  1. Open a report with multiple expenses.
  2. Click on the More menu for one expense and select Reject.
  3. Enter a reject reason and confirm.
  4. Verify the rejected expense is moved to Self DM.
  5. Verify no violation is added.
  6. Verify system messages are added to the original report and the transaction thread.

Test 2: Single expense

  1. Open a report with only one expense.
  2. Click on the More menu for that expense and select Reject.
  3. Enter a reject reason and confirm.
  4. Verify the expense is moved to Self DM.
  5. Verify the original expense report is deleted.
  6. Verify system messages are added to the transaction thread only.

E. IOU – Reject option, no Mark as Resolved
Precondition:

  • Logged in as an approver or IOU recipient.
  • IOU expense exists in DM.

Test:

  1. Open the IOU expense in DM.
  2. Click on the More menu and select Reject.
  3. Enter a comment and confirm.
  4. Verify the expense is unreported to Self DM.
  5. Verify the expense thread is marked unread for the submitter.
  6. Verify the reject system message and comment are added in the transaction thread.
  7. Verify no violation is added and there is not mark as resolved button.

F. Mark as Resolved – Violation
Precondition:

  • Logged in as the submitter of a rejected expense with a violation reason.

Test:

  1. Open the rejected expense details view.
  2. Submitter should be able to see the rejected reason in the thread.
  3. If there are more than one expense, then the reason should be visible as an error message below each expense in the expense report.
  4. Open the reports section, the submitter should be able to see rejected message in the reports section as well.
  5. Open the single expense view, and verify the Mark as resolved button is the primary action.
  6. Click on Mark as resolved.
  7. Verify the violation disappears locally from the expense.
  8. Verify a system message is added locally: marked the rejection reason as resolved.
  9. Reconnect internet and verify the server confirms the violation resolution.
  10. If server fails the request, verify the violation is restored and the system message is removed.
  • Verify that no errors appear in the JS console

Offline tests

Offline Tests


A. Hold vs Reject Modal
Precondition:

  • Logged in as an approver or workspace admin.
  • An expense is present in a report and can be rejected.
  • NVP nvp_dismissedRejectUseExplanation is not set in the account.
  • Device is offline.

Test:

  1. Open the expense details view.
  2. Click on the More menu and select Reject.
  3. Verify that the Hold vs Reject modal is opened.
  4. Click Got it in the modal.
  5. The modal should close and the reject reason page should open.
  6. Verify nvp_dismissedRejectUseExplanation is set optimistically to true.
  7. Exit out of the report and repeat the steps from 1 again.
  8. This time the modal shouldn't open and the reject reason page should open directly. All future rejections should skip the modal.
  9. Reconnect to the internet and verify that NVP is synced to the server.

B. Delayed Submission Workspace – Multiple expenses – Existing draft report available
Precondition:

  • Logged in as an approver or workspace admin.
  • Workspace has Delayed submission enabled.
  • Current report has multiple expenses.
  • Another draft report exists in the same workspace by the submitter.
  • Device is offline.

Test:

  1. Open a report with multiple expenses.
  2. Click on the More menu for one expense and select Reject.
  3. Enter a reject reason and confirm.
  4. Verify the expense is removed from the original report locally.
  5. Verify the expense is moved to the existing draft report locally.
  6. Verify the original report total is updated locally.
  7. Verify system messages are added to both the original report and the transaction thread locally:
    a. rejected this expense is the first message added to the report.
    b. Rejectd reason as a comment is the next message.
    c. Moved to report is the last system message.
  8. Reconnect internet and verify that all changes are synced to the server.

Need to verify from Product here
C. Delayed Submission Workspace – Multiple expenses – No existing draft report
Precondition:

  • Logged in as an approver or workspace admin.
  • Workspace Scheduled Submit enabled, frequency != Instant.
  • Current report has multiple expenses.
  • No draft report exists in the workspace.
  • Device is offline.

Test:

  1. Open a report with multiple expenses.
  2. Click on the More menu for one expense and select Reject.
  3. Enter a reject reason and confirm.
  4. Verify the expense is removed from the original report locally.
  5. Verify a new draft report is created locally and the expense is moved there.
  6. Verify the original report total is updated locally.
  7. Verify system messages are added locally to both the original report and the transaction thread.
    They are the same as in test B.
  8. Reconnect internet and verify that all changes are synced to the server.

D. Instant Submission Workspace – Single expense and multiple expenses, and IOU test
Precondition:

  • Logged in as an approver or workspace admin.
  • Workspace Delayed submission is disabled.
  • Device is offline.
  • The same flow for IOU (1:1 chat).

Test 1: Multiple expenses

  1. Open a report with multiple expenses.
  2. Click on the More menu for one expense and select Reject.
  3. Enter a reject reason and confirm.
  4. Verify the rejected expense is removed from the expense list.
  5. If it's a single expense, then the expense report should be completely removed.
  6. Verify no violation is added locally.
  7. Verify system messages are added locally to the original report and the transaction thread.
  8. Reconnect internet and verify that all changes are synced to the server.

QA Steps

Same as tests.

// TODO: These must be filled out, or the issue title must include "[No QA]."

  • 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
    • MacOS: Desktop
  • 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 there are no new alerts related to the canBeMissing param for useOnyx
  • I followed proper code patterns (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 shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • 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)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • 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)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • 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.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • 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: HybridApp
android-rejection.mov
Android: mWeb Chrome
mweb-chrome-rejection.mov
iOS: HybridApp
iOS: mWeb Safari
mweb-safari-rejection.mov
MacOS: Chrome / Safari

Web Hold vs Reject Modal

hold-vs-reject-modal

Web Reject with Delayed Submission for Single Expense Report

web-single-expense-rejection.mov

Web Reject with delayed submission for multiple expenses report and other existing report

web-multiple-expense-rejection.mov

Web Reject with delayed submission for multiple expenses report without another existing report

web-existing-report-rejection.mov

Web Reject Instant Submission

web-instant-rejection.mov
MacOS: Desktop
desktop-rejection.mov
Old Videos

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native

iOS and mWeb Safari Rejectd Instant Submission

ios-mweb-safari-declined-instant-submisison.mov

iOS and mWeb Safari Rejectd Delayed Submission

ios-mweb-safari-decline-delayed-submisison_8E0lGIfm.mp4
iOS: mWeb Safari
MacOS: Chrome / Safari

Web Hold vs Reject Modal

web-hold-decline-modal.mov

Web Reject IOU
https://github.com/user-attachments/assets/7a257211-1677-4b50-b869-6163929efc65

Web Reject with Delayed Submission for Single Expense Report

web-decline-delayed-submission-single-expense.mov

Web Reject with delayed submission for multiple expenses report and other existing report

web-decline-delayed-submission-multiple-expense-with-existing-report.mov

Web Reject with delayed submission for multiple expenses report without another existing report

web-decline-delayed-submission-multiple-expense-no-existing-report.mov

Web Reject Instant Submission

web-decline-instant-submission_5JgmlPlb.mp4
MacOS: Desktop

Desktop Approver Reject

desktop-approver-decline_iljI2BEB.mp4

Desktop Submitter Mark as Resolved

desktop-submitter-mark-as-resolved_VI0l1KPd.mp4

@OSBotify
Copy link
Contributor

🚀 Deployed to production by https://github.com/Beamanator in version: 9.2.14-7 🚀

platform result
🖥 desktop 🖥 success ✅
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Comment on lines +12135 to +12140
// For reports with single expense: Delete the report
optimisticData.push({
onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: null,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

When reject single expense, we should also unlink report from transaction, as well as deleting the report optimistically.
More context: #72571 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

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.