Skip to content

[Due for payment 2025-09-19] [$250] Scan - Image can be dropped on scan zone in room when there are 2 members which leads to error #66643

Closed

Description

@vincdargento

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.82-2
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from TestRail: #62650
Email or phone of affected tester (no customers): jsdnoisjiosdjdoi@gmail.com
Issue reported by: Applause Internal Team
Device used: Mac 15.5 / Chrome
App Component: Money Requests

Action Performed:

  1. Go to staging.expensify.com
  2. Open FAB > Start chat > Room.
  3. Create a room.
  4. Open room header > Members.
  5. Invite two members to the room.
  6. Close the RHP.
  7. Drag and drop an image to Scan zone.
  8. Click Create expense on the confirm page.
  9. Wait for error to show up on the expense preview.
  10. Click on the expense preview.

Expected Result:

In Step 7, Scan zone should be disabled in room if split expense cannot be created in room via drag and drop.

Actual Result:

In Step 7, Scan zone is not disabled in room and photo can be dropped on the Scan zone.
In Step 10, error shows up because the scan expense cannot be created in room via drop zone when there are at least 2 members in the room.

If only one member is invited to the room, Scan expense is created in 1:1 DM when the photo is dropped on Scan zone in the room.

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari
  • MacOS: Desktop

Screenshots/Videos

bug.mp4

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021947236346996745937
  • Upwork Job ID: 1947236346996745937
  • Last Price Increase: 2025-08-04
  • Automatic offers:
    • Krishna2323 | Contributor | 108293324
Issue OwnerCurrent Issue Owner: @Krishna2323

Activity

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

    @melvin-bot

    Triggered auto assignment to @sonialiap (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. samranahm commented on Jul 17, 2025

    @samranahm
    Contributor

    Proposal

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

    Image can be dropped on scan zone in room when there are 2 members which leads to error

    What is the root cause of that problem?

    We are missing room check here

    const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && !isDistanceRequest(transaction);

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

    Add the room check here

    const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && !isDistanceRequest(transaction);

    to prevent showing the receipt option in rooms

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

    NA

    What alternative solutions did you explore? (Optional)

    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.

  4. ryntgh commented on Jul 17, 2025

    @ryntgh
    Contributor

    Proposal

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

    Scan - Image can be dropped on scan zone in room when there are 2 members which leads to error

    What is the root cause of that problem?

    shouldDisplayDualDropZone only checks if money request options are available but doesn't validate whether scan requests can actually be processed in the current report

    const shouldDisplayDualDropZone = useMemo(() => {
    const parentReport = getParentReport(report);
    const isSettledOrApproved = isSettled(report) || isSettled(parentReport) || isReportApproved({report}) || isReportApproved({report: parentReport});
    return (shouldAddOrReplaceReceipt && !isSettledOrApproved) || !!temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs).length;
    }, [shouldAddOrReplaceReceipt, report, policy, reportParticipantIDs]);

    The temporary_getMoneyRequestOptions function returns CONST.IOU.TYPE.SPLIT for rooms with multiple participants, which makes shouldDisplayDualDropZone return true and shows the scan drop zone

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

    We can add validation to check for isChatRoom

        const shouldDisplayDualDropZone = useMemo(() => {
            const parentReport = getParentReport(report);
            const isSettledOrApproved = isSettled(report) || isSettled(parentReport) || isReportApproved({report}) || isReportApproved({report: parentReport});
            const hasMoneyRequestOptions = !!temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs).length;
            return ((shouldAddOrReplaceReceipt && !isSettledOrApproved) || hasMoneyRequestOptions) && !isChatRoom(report);
        }, [shouldAddOrReplaceReceipt, report, policy, reportParticipantIDs]);

    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

  5. Krishna2323 commented on Jul 17, 2025

    @Krishna2323
    Contributor

    Proposal

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

    Scan - Image can be dropped on scan zone in room when there are 2 members which leads to error

    What is the root cause of that problem?

    • The iou type is always CONST.IOU.TYPE.SUBMIT if the isSelfDM is false.
      const newTransactionID = newTransaction?.transactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID;
      setMoneyRequestReceipt(newTransactionID, source, file.name ?? '', true);
      setMoneyRequestParticipantsFromReport(newTransactionID, report);
      });
      Navigation.navigate(
      ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(
      CONST.IOU.ACTION.CREATE,
      isSelfDM(report) ? CONST.IOU.TYPE.TRACK : CONST.IOU.TYPE.SUBMIT,
      CONST.IOU.OPTIMISTIC_TRANSACTION_ID,
      reportID,
      ),
      );
      }
      };

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

    • We should check if the report is a group chat or a room chat with multiple members and change the IOU type to split:
                const allParticipants = getParticipantsAccountIDsForDisplay(report, false, true, undefined, reportMetadata);
                const hasMultipleParticipants = allParticipants.length > 1;
                const shouldSplit = isGroupChat(report) || (isChatRoom(report) && hasMultipleParticipants);
                const defaultIOUType = shouldSplit ? CONST.IOU.TYPE.SPLIT : CONST.IOU.TYPE.SUBMIT;
                Navigation.navigate(
                    ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(
                        CONST.IOU.ACTION.CREATE,
                        isSelfDM(report) ? CONST.IOU.TYPE.TRACK : defaultIOUType,
                        CONST.IOU.OPTIMISTIC_TRANSACTION_ID,
                        reportID,
                    ),
                );
            }

    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)

    • We can create a util function and for each type of report, we will return the default iou action accordingly.
    • We should use the getMoneyRequestOptions function for reference and return a default iou type for each type of report if scanning is allowed in that specific type of report. e.g. return iou type create for workspace expense chat, split for room/group chat with multiple participants, null for invoice report chat.

      App/src/libs/ReportUtils.ts

      Lines 8589 to 8674 in be6876c

      function getMoneyRequestOptions(report: OnyxEntry<Report>, policy: OnyxEntry<Policy>, reportParticipants: number[], filterDeprecatedTypes = false): IOUType[] {
      const teacherUnitePolicyID = environment === CONST.ENVIRONMENT.PRODUCTION ? CONST.TEACHERS_UNITE.PROD_POLICY_ID : CONST.TEACHERS_UNITE.TEST_POLICY_ID;
      const isTeachersUniteReport = report?.policyID === teacherUnitePolicyID;
      // This will get removed as part of https://github.com/Expensify/App/issues/59961
      // eslint-disable-next-line deprecation/deprecation
      const reportNameValuePairs = getReportNameValuePairs(report?.reportID);
      // In any thread, task report or trip room, we do not allow any new expenses
      if (isChatThread(report) || isTaskReport(report) || isInvoiceReport(report) || isSystemChat(report) || isArchivedReport(reportNameValuePairs) || isTripRoom(report)) {
      return [];
      }
      if (isInvoiceRoom(report)) {
      if (canSendInvoiceFromWorkspace(policy?.id) && isPolicyAdmin(report?.policyID, allPolicies)) {
      return [CONST.IOU.TYPE.INVOICE];
      }
      return [];
      }
      // We don't allow IOU actions if an Expensify account is a participant of the report, unless the policy that the report is on is owned by an Expensify account
      const doParticipantsIncludeExpensifyAccounts = lodashIntersection(reportParticipants, CONST.EXPENSIFY_ACCOUNT_IDS).length > 0;
      // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850
      // eslint-disable-next-line deprecation/deprecation
      const policyOwnerAccountID = getPolicy(report?.policyID)?.ownerAccountID;
      const isPolicyOwnedByExpensifyAccounts = policyOwnerAccountID ? CONST.EXPENSIFY_ACCOUNT_IDS.includes(policyOwnerAccountID) : false;
      if (doParticipantsIncludeExpensifyAccounts && !isPolicyOwnedByExpensifyAccounts) {
      // Allow create expense option for Manager McTest report
      if (reportParticipants.some((accountID) => accountID === CONST.ACCOUNT_ID.MANAGER_MCTEST) && Permissions.isBetaEnabled(CONST.BETAS.NEWDOT_MANAGER_MCTEST, allBetas)) {
      return [CONST.IOU.TYPE.SUBMIT];
      }
      return [];
      }
      const otherParticipants = reportParticipants.filter((accountID) => currentUserPersonalDetails?.accountID !== accountID);
      const hasSingleParticipantInReport = otherParticipants.length === 1;
      let options: IOUType[] = [];
      if (isSelfDM(report)) {
      options = [CONST.IOU.TYPE.TRACK];
      }
      if (canRequestMoney(report, policy, otherParticipants)) {
      // For Teachers Unite policy, don't show Create Expense option
      if (!isTeachersUniteReport) {
      options = [...options, CONST.IOU.TYPE.SUBMIT];
      if (!filterDeprecatedTypes) {
      options = [...options, CONST.IOU.TYPE.REQUEST];
      }
      }
      // If the user can request money from the workspace report, they can also track expenses
      if (isPolicyExpenseChat(report) || isExpenseReport(report)) {
      options = [...options, CONST.IOU.TYPE.TRACK];
      }
      }
      // For expense reports on Teachers Unite workspace, disable "Create report" option
      if (isExpenseReport(report) && report?.policyID === teacherUnitePolicyID) {
      options = options.filter((option) => option !== CONST.IOU.TYPE.SUBMIT);
      }
      // User created policy rooms and default rooms like #admins or #announce will always have the Split Expense option
      // unless there are no other participants at all (e.g. #admins room for a policy with only 1 admin)
      // DM chats will have the Split Expense option.
      // Your own expense chats will have the split expense option.
      // Only show Split Expense for TU policy
      if (
      (isChatRoom(report) && !isAnnounceRoom(report) && otherParticipants.length > 0) ||
      (isDM(report) && otherParticipants.length > 0) ||
      (isGroupChat(report) && otherParticipants.length > 0) ||
      (isPolicyExpenseChat(report) && report?.isOwnPolicyExpenseChat && isTeachersUniteReport)
      ) {
      options = [...options, CONST.IOU.TYPE.SPLIT];
      }
      // Pay someone option should be visible only in 1:1 DMs
      if (isDM(report) && hasSingleParticipantInReport) {
      options = [...options, CONST.IOU.TYPE.PAY];
      if (!filterDeprecatedTypes) {
      options = [...options, CONST.IOU.TYPE.SEND];
      }
      }
      return options;
      }
    • The update the condition below to use the new util function and check if any default option is returned or not, instead of checking the length of the getMoneyRequestOptions.
      const shouldDisplayDualDropZone = useMemo(() => {
      const parentReport = getParentReport(report);
      const isSettledOrApproved = isSettled(report) || isSettled(parentReport) || isReportApproved({report}) || isReportApproved({report: parentReport});
      return (shouldAddOrReplaceReceipt && !isSettledOrApproved) || !!temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs).length;
      }, [shouldAddOrReplaceReceipt, report, policy, reportParticipantIDs]);

    What alternative solutions did you explore? (Optional 2)

    • We can check if the last iou type returned from getMoneyRequestOptions can work with scanning or not and based on that we will set the iou type when evaluating shouldDisplayDualDropZone and navigating to confirmation page.
    • We can create a array of options which can work with scanning, e.g. ["create", "split"] and then check if the last iou type returned from getMoneyRequestOptions is an valid option in the array or not.
        function getPreferredScannableIOUType(iouOptions: IOUType[]): IOUType | undefined {
            const preferredOrder: IOUType[] = [CONST.IOU.TYPE.TRACK, CONST.IOU.TYPE.SUBMIT, CONST.IOU.TYPE.SPLIT];
    
            return preferredOrder.find((type) => iouOptions.includes(type) && (type !== 'track' || isSelfDM(report)));
        }
        const scannableOption = getPreferredScannableIOUType(temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs));
    
        const shouldDisplayDualDropZone = useMemo(() => {
            const parentReport = getParentReport(report);
            const isSettledOrApproved = isSettled(report) || isSettled(parentReport) || isReportApproved({report}) || isReportApproved({report: parentReport});
    
            return (shouldAddOrReplaceReceipt && !isSettledOrApproved) || !!scannableOption;
        }, [shouldAddOrReplaceReceipt, report, scannableOption]);
    
        Navigation.navigate(
            ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, scannableOption ?? CONST.IOU.TYPE.SUBMIT, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, reportID),
        );

    Result

    Monosnap.screencast.2025-07-18.11-04-39.mp4
  6. Krishna2323 commented on Jul 17, 2025

    @Krishna2323
    Contributor
  7. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Jul 21, 2025
  8. changed the title [-]Scan - Image can be dropped on scan zone in room when there are 2 members which leads to error[/-] [+][$250] Scan - Image can be dropped on scan zone in room when there are 2 members which leads to error[/+] on Jul 21, 2025
  9. melvin-bot commented on Jul 21, 2025

    @melvin-bot
  10. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Jul 21, 2025
  11. melvin-bot commented on Jul 21, 2025

    @melvin-bot

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

  12. 60 remaining items

  13. sonialiap commented on Sep 22, 2025

    @sonialiap
    Contributor

    Payment summary:
    @hoangzinh $250 please request in NewDot and complete the checklist
    @Krishna2323 $250 please request in NewDot to be paid in upwork - I see the offer was rejected, was that by accident?

  14. hoangzinh commented on Sep 22, 2025

    @hoangzinh
    Contributor

    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:
    • [Contributor] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.

      Link to comment: https://github.com/Expensify/App/pull/65264/files#r2367777423

    • [Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.

      Link to discussion: N/A

    • [Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.

    • [BugZero Assignee] Create a GH issue for creating/updating the regression test once above steps have been agreed upon.

      Link to issue:

    Regression Test Proposal

    Note: Tests are only for Web and Desktop app, mWeb and native app does not support drag and drop

    Test:

    1. Sign in to ND
    2. Go to any room chat
    3. Drag and drop an image to Scan zone
    4. Verify that only attachment drop area is shown
    5. Go to a group chat
    6. Drag and drop an image to Scan zone
    7. Verify that only attachment drop area is shown

    Do we agree 👍 or 👎

  15. hoangzinh commented on Sep 22, 2025

    @hoangzinh
    Contributor

    Thanks @sonialiap. I requested in ND

  16. Krishna2323 commented on Sep 23, 2025

    @Krishna2323
    Contributor

    @sonialiap I'm now eligible for payments through NewDot that's why I ended the Upwork contract.

    Ref: #64948 (comment)

  17. sonialiap commented on Sep 25, 2025

    @sonialiap
    Contributor

    @Krishna2323 congrats! I've updated the payment summary asking you to request payment in NewDot

  18. melvin-bot commented on Sep 26, 2025

    @melvin-bot

    @Krishna2323 Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

  19. Krishna2323 commented on Oct 4, 2025

    @Krishna2323
    Contributor

    Requested in NewDot!

  20. garrettmknight commented on Oct 7, 2025

    @garrettmknight
    Contributor

    $250 approved for @Krishna2323

  21. quinthar commented on Oct 18, 2025

    @quinthar
    Contributor

    Approved $250 to @hoangzinh

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

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions