Skip to content

[$250] Track - Whisper options appears on workspace chat when Share it with your accountant #71212

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.2.17-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: https://test-management.browserstack.com/projects/2219752/test-runs/TR-898/folder/13176877/41237356/912950916
Email or phone of affected tester (no customers): applausetester+gm1038177@applause.expensifail.com
Issue reported by: Applause Internal Team
Device used: Lenovo 80ES / Windows 10 Pro
App Component: Other

Action Performed:

Preconditions:

  1. User have no workspace

Steps:

  1. Go to old dot> Login with Expensifail account> Create unreported manual expense
  2. Go to staging.new.expensify.com> Login with same account
  3. Go to Self DM chat> Create expense
  4. From the whisper options select Share it with your accountant
  5. Select an accountant> Complete the process
  6. Verify that the transaction is moved to the new workspace

Expected Result:

There should be no whisper options on workspace chat expense

Actual Result:

Whisper options appears on workspace chat when Share it with your accountant

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/~021970973512761844896
  • Upwork Job ID: 1970973512761844896
  • Last Price Increase: 2025-10-01
  • Automatic offers:
    • mkzie2 | Contributor | 109029849
Issue OwnerCurrent Issue Owner: @twisterdotcom

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Sep 24, 2025
  2. melvin-bot commented on Sep 24, 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. abzokhattab commented on Sep 24, 2025

    @abzokhattab
    Contributor

    Proposal

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

    Whisper options (including "Share it with your accountant") incorrectly appear on workspace chat expenses when they should be hidden.

    What is the root cause of that problem?

    • The isActionableTrackExpense logic in PureReportActionItem.tsx shows whisper options for all actionable track expense actions without checking if the current report is a workspace chat
    • The code lacks a condition to differentiate between workspace chats (POLICY_EXPENSE_CHAT) and regular chats when determining whether to display whisper options

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

    • Add a check using isPolicyExpenseChat(report) under this line to return an empty array when the report is a workspace chat to prevent whisper options from appearing
           if (isPolicyExpenseChat(report)) {
                    return [];
                }

    POC

    Screen.Recording.2025-09-25.at.01.45.22.mov

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

    Add a test should not show whisper options for actionable track expense in workspace chat:

     describe('Actionable track expense whisper options', () => {
            it('should not show whisper options for actionable track expense in workspace chat', async () => {
                const action = createReportAction(CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_TRACK_EXPENSE_WHISPER, {
                    transactionID: '12345',
                });
    
                // Create a workspace chat report (policy expense chat)
                const workspaceReport = {
                    reportID: '123',
                    type: CONST.REPORT.TYPE.CHAT,
                    chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT,
                    policyID: 'policy123',
                };
    
                renderItemWithAction(action, workspaceReport);
                await waitForBatchedUpdatesWithAct();
    
                expect(screen.queryByText(translateLocal('actionableMentionTrackExpense.submit'))).not.toBeOnTheScreen();
                expect(screen.queryByText(translateLocal('actionableMentionTrackExpense.share'))).not.toBeOnTheScreen();
                expect(screen.queryByText(translateLocal('actionableMentionTrackExpense.categorize'))).not.toBeOnTheScreen();
                expect(screen.queryByText(translateLocal('actionableMentionTrackExpense.nothing'))).not.toBeOnTheScreen();
            });
    
            it('should show whisper options for actionable track expense in regular chat', async () => {
                const action = createReportAction(CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_TRACK_EXPENSE_WHISPER, {
                    transactionID: '12345',
                });
    
                const regularReport = {
                    reportID: '123',
                    type: CONST.REPORT.TYPE.CHAT,
                    chatType: CONST.REPORT.CHAT_TYPE.SELF_DM,
                };
    
                renderItemWithAction(action, regularReport);
                await waitForBatchedUpdatesWithAct();
    
                expect(screen.getByText(translateLocal('actionableMentionTrackExpense.submit'))).toBeOnTheScreen();
                expect(screen.getByText(translateLocal('actionableMentionTrackExpense.nothing'))).toBeOnTheScreen();
            });
        });

    What alternative solutions did you explore? (Optional)

    NA

  4. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Sep 24, 2025
  5. melvin-bot commented on Sep 24, 2025

    @melvin-bot
  6. changed the title [-]Track - Whisper options appears on workspace chat when Share it with your accountant[/-] [+][$250] Track - Whisper options appears on workspace chat when Share it with your accountant[/+] on Sep 24, 2025
  7. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Sep 24, 2025
  8. melvin-bot commented on Sep 24, 2025

    @melvin-bot

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

  9. mkzie2 commented on Sep 25, 2025

    @mkzie2
    Contributor

    Proposal

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

    Whisper options appears on workspace chat when Share it with your accountant

    What is the root cause of that problem?

    If the user has no workspace, a draft report is created when share it with your accountant. When retrieving participants, we're obtaining the report from Onyx.connect here, but it's not updated when the draft report is merged, resulting in an incorrect participant. We can see in the UI that an unavailable workspace is displayed on the confirmation page.

    const participants = useMemo(
    () =>
    transaction?.participants?.map((participant) => {
    if (participant.isSender && iouType === CONST.IOU.TYPE.INVOICE) {
    return participant;
    }
    return participant.accountID ? getParticipantsOption(participant, personalDetails) : getReportOption(participant, reportAttributesDerived);
    }) ?? [],
    [transaction?.participants, iouType, personalDetails, reportAttributesDerived],
    );
    const isPolicyExpenseChat = useMemo(() => participants?.some((participant) => participant.isPolicyExpenseChat), [participants]);

    Then the actionableTrackExpenseWhisper is created because isPolicyExpenseChat is false.

    App/src/libs/actions/IOU.ts

    Lines 4002 to 4009 in ffe38b1

    if (!isPolicyExpenseChat) {
    actionableTrackExpenseWhisper = buildOptimisticActionableTrackExpenseWhisper(iouAction, optimisticTransaction.transactionID);
    }
    // STEP 5: Build Onyx Data
    const trackExpenseOnyxData = buildOnyxDataForTrackExpense({
    participant,
    chat: {report: chatReport, previewAction: reportPreviewAction},

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

    1. We should pass the draft report to getReportOption
    const [allReportDrafts] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT, {canBeMissing: true});
    const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true});
    const getReportOrDraft = useCallback((id?: string) => allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`] ?? allReportDrafts?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${id}`], [allReports, allReportDrafts]);
    const participants = useMemo(
        () =>
            transaction?.participants?.map((participant) => {
                if (participant.isSender && iouType === CONST.IOU.TYPE.INVOICE) {
                    return participant;
                }
                const reportOrDraft = getReportOrDraft(participant?.reportID ?? '');
                return participant.accountID ? getParticipantsOption(participant, personalDetails) : getReportOption(participant, reportAttributesDerived, reportOrDraft);
            }) ?? [],
        [transaction?.participants, iouType, personalDetails, reportAttributesDerived, getReportOrDraft],
    );
    function getReportOption(participant: Participant, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], reportOrDraft?: OnyxEntry<Report>): OptionData {
        const report = getReportOrDraftReport(participant.reportID) ?? reportOrDraft;
    

    const participants = useMemo(
    () =>
    transaction?.participants?.map((participant) => {
    if (participant.isSender && iouType === CONST.IOU.TYPE.INVOICE) {
    return participant;
    }
    return participant.accountID ? getParticipantsOption(participant, personalDetails) : getReportOption(participant, reportAttributesDerived);
    }) ?? [],
    [transaction?.participants, iouType, personalDetails, reportAttributesDerived],
    );
    const isPolicyExpenseChat = useMemo(() => participants?.some((participant) => participant.isPolicyExpenseChat), [participants]);

    1. After this issue is fixed, we still have another problem where the report name of the iouReport is incorrect.
    Image

    The RCA is the policy is undefined here since it's draft

    const policy = getPolicy(policyID);

    Then we can't get the title field here.

    const titleReportField = getTitleReportField(getReportFieldsByPolicyID(policyID) ?? {});

    To fix this issue

    • We need to add the title field for the draft workspace
    fieldList: {
        [CONST.POLICY.FIELDS.FIELD_LIST_TITLE]: {
            defaultValue: CONST.POLICY.DEFAULT_REPORT_NAME_PATTERN,
            pendingFields: {defaultValue: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, deletable: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD},
            type: CONST.POLICY.DEFAULT_FIELD_LIST_TYPE,
            target: CONST.POLICY.DEFAULT_FIELD_LIST_TARGET,
            name: CONST.POLICY.DEFAULT_FIELD_LIST_NAME,
            fieldID: CONST.POLICY.FIELDS.FIELD_LIST_TITLE,
            deletable: true,
        },
    },
    

    const optimisticData: OnyxUpdate[] = [

    • Add policy parameter to buildOptimisticExpenseReport function and pass it here
    function buildOptimisticExpenseReport(
        chatReportID: string | undefined,
        policyID: string | undefined,
        payeeAccountID: number,
        total: number,
        currency: string,
        nonReimbursableTotal = 0,
        parentReportActionID?: string,
        optimisticIOUReportID?: string,
        reportPolicy?: OnyxEntry<Policy>,
    ) {
    ...
       const policyName = reportPolicy?.name ?? getPolicyName({report});
       const policy = reportPolicy ?? getPolicy(policyID);
       const titleReportField = getTitleReportField(policy?.fieldList ?? {});
       if (!!titleReportField && isPaidGroupPolicyPolicyUtils(policy)) {
            expenseReport.reportName = populateOptimisticReportFormula(titleReportField.defaultValue, expenseReport, policy);
        }
    }
    
    

    const titleReportField = getTitleReportField(getReportFieldsByPolicyID(policyID) ?? {});

    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)

    NA

    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.

  10. melvin-bot commented on Sep 29, 2025

    @melvin-bot

    @Pujan92 Eep! 4 days overdue now. Issues have feelings too...

  11. 48 remaining items

  12. m-natarajan commented on Nov 12, 2025

    @m-natarajan

    Blocked from reproducing this as "Share with accountant" option is not displayed, Tried both public and expensifail domain.
    Windows 11 / chrome / v9.2.55-3

    Image
  13. Pujan92 commented on Nov 14, 2025

    @Pujan92
    Contributor

    Yes, it is disabled in PR. I think we need to wait until it is enabled again or else @aldo-expensify can verify in dev mode by enabling it in the code.

  14. mkzie2 commented on Nov 23, 2025

    @mkzie2
    Contributor

    @aldo-expensify Can you please check the comment above?

  15. aldo-expensify commented on Nov 25, 2025

    @aldo-expensify
    Contributor

    Yes, it is disabled in PR. I think we need to wait until it is enabled again or else @aldo-expensify can verify in dev mode by enabling it in the code.

    hmm, I'm understanding it is not certain that the functionality will be enabled again, if that is the case, maybe we should just close all this and pay for the work done. What do you think @cristipaval ?

  16. cristipaval commented on Nov 25, 2025

    @cristipaval
    Contributor

    I don't think it makes sense to spend effort on those flows. We disabled them for unlimited time.

  17. twisterdotcom commented on Nov 25, 2025

    @twisterdotcom
    Contributor

    Okay, if we're not recommending going ahead, let's close and pay out for anybody who has written a PR and anybody who has reviewed a PR anyway.

  18. Pujan92 commented on Nov 28, 2025

    @Pujan92
    Contributor

    In that case, @twisterdotcom can you plz add a payment summary and close the issue?

  19. twisterdotcom commented on Nov 28, 2025

    @twisterdotcom
    Contributor

    Payment Summary:

  20. trjExpensify commented on Dec 1, 2025

    @trjExpensify
    Contributor

    approved $250 for @Pujan92.

  21. mkzie2 commented on Jan 20, 2026

    @mkzie2
    Contributor

    @twisterdotcom @trjExpensify Hi, my account is not eligible for NewDot payment yet. Please handle payment via the existing Upwork contract.

  22. twisterdotcom commented on Jan 20, 2026

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

Metadata

Metadata

Labels

BugSomething is broken. Auto assigns a BugZero manager.ExternalAdded to denote the issue can be worked on by a contributorReviewingHas a PR in reviewWeeklyKSv2

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions