[$500] iOS - The concierge task is not auto-completed after completing Test drive via Reports tab
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: 9.2.71-5 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/Expensify/issues/536068 Email or phone of affected tester (no customers): N/A Issue reported by: Applause Internal Team Device used: Apple iPhone 13 - iOS 18.6.2 App Component: Other
Action Performed:
Prerequisite: Invite a brand new account to a workspace
- Open the app
- Sign-in with the invited user
- Go to the Reports tab > Expenses.
- Tap the "Take a test drive" button
- Finish Test Drive
- Tap Get Started
- Navigate to the Concierge chat
Expected Result:
The Test Drive task in the Concierge chat should be completed
Actual Result:
The concierge task is not auto-complete after the invited employee completes the Test drive via Reports tab
Workaround:
Unknown
Platforms:
- [ ] Android: App
- [ ] Android: mWeb Chrome
- [x] iOS: App
- [ ] iOS: mWeb Safari
- [ ] iOS: mWeb Chrome
- [ ] Windows: Chrome
- [ ] MacOS: Chrome / Safari
- [ ] MacOS: Desktop
Screenshots/Videos
https://github.com/user-attachments/assets/a38d8c66-7088-4dcc-b26f-35376542187d
Upwork Automation - Do Not Edit
- Upwork Job URL: https://www.upwork.com/jobs/~021996584871222053906
- Upwork Job ID: 1996584871222053906
- Last Price Increase: 2025-12-25
Issue Owner
Current Issue Owner: @shubham1206agra
Triggered auto assignment to @trjExpensify (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.
Job added to Upwork: https://www.upwork.com/jobs/~021996584871222053906
Triggered auto assignment to Contributor-plus team member for initial proposal review - @shubham1206agra (External)
π¨ Edited by proposal-police: This proposal was edited at 2025-12-18 13:31:55 UTC.
Proposal
Please re-state the problem that we are trying to solve in this issue.
The concierge task is not completed after completing test drive via Reports tab.
What is the root cause of that problem?
The Onyx optimistic onboarding data is coupled with the openReport function.
This means, if openReport never executes, or it executes before it's supposed to (before OpenApp), the optimistic onboarding data, and therefore, its tasks (which are fully optimistic), are never created.
Currently, there are two scenarios where this happens: Scenario 1 (this issue):
- Invite a brand new account (A) through the workspace.
- Log into Account A manually and open the app in a narrow screen (no report is opened).
- Go straight to Reports > Expenses > "Take a test drive".
- Finish the task and go to Concierge.
- Notice the task in Concierge is not crossed off.
Scenario 2:
- Invite a brand new account through the workspace.
- Click on the "Get started" link in the invite email (either narrow or wide screen is fine).
- Go to Reports > Expenses > "Take a test drive".
- Finish the task and go to Concierge.
- Notice the task in Concierge is not crossed off.
Scenario 1 happens because openReport is never executed.
Scenario 2 happens because OpenReport executes before OpenApp which makes the introSelected variable undefined, which means the onboarding Onyx data is also never created.
All of this means this doesn't ever get executed, which is why we have to decouple it from openReport:
https://github.com/Expensify/App/blob/4625fac8c6f6e0f1109fc4b103ed28791551da9a/src/libs/actions/Report.ts#L1190-L1194
What changes do you think we should make in order to solve the problem?
Since this is a very specific edge case, I think it's a good complexity<->correctness tradeoff to simply open the Concierge report if we do end up in this situation.
-
Remove these lines from
openReport: https://github.com/Expensify/App/blob/549b140c93d50ab1cfaaa7b8f20db36dd74e088b/src/libs/actions/Report.ts#L1160-L1204 -
Create and export this function in
src/libs/actions/Report.ts:
function openOnboardingDataOnce(conciergeReportID: string, introSelected: IntroSelected) {
const optimisticData: OnyxUpdate[] = [];
const successData: OnyxUpdate[] = [];
const failureData: OnyxUpdate[] = [];
const finallyData: OnyxUpdate[] = [];
const parameters: OpenReportParams = {
reportID: conciergeReportID
};
const isInviteOnboardingComplete = introSelected?.isInviteOnboardingComplete ?? false;
const isOnboardingCompleted = onboarding?.hasCompletedGuidedSetupFlow ?? false;
// Some cases we can have two open report requests with guide setup data because isInviteOnboardingComplete is not updated completely.
// Then we need to check the list request and prevent the guided setup data from being duplicated.
const allPersistedRequests = getAll();
const hasOpenReportWithGuidedSetupData = allPersistedRequests.some((request) => request.command === WRITE_COMMANDS.OPEN_REPORT && request.data?.guidedSetupData);
// Prepare guided setup data only when nvp_introSelected is set and onboarding is not completed
// OldDot users will never have nvp_introSelected set, so they will not see guided setup messages
if (!isOnboardingCompleted && !isInviteOnboardingComplete && !hasOpenReportWithGuidedSetupData) {
const {choice, inviteType} = introSelected;
const isInviteIOUorInvoice = inviteType === CONST.ONBOARDING_INVITE_TYPES.IOU || inviteType === CONST.ONBOARDING_INVITE_TYPES.INVOICE;
const isInviteChoiceCorrect = choice === CONST.ONBOARDING_CHOICES.ADMIN || choice === CONST.ONBOARDING_CHOICES.SUBMIT || choice === CONST.ONBOARDING_CHOICES.CHAT_SPLIT;
if (isInviteChoiceCorrect && !isInviteIOUorInvoice) {
const onboardingMessage = getOnboardingMessages().onboardingMessages[choice];
if (choice === CONST.ONBOARDING_CHOICES.CHAT_SPLIT) {
const updatedTasks = onboardingMessage.tasks.map((task) => (task.type === 'startChat' ? {...task, autoCompleted: true} : task));
onboardingMessage.tasks = updatedTasks;
}
const onboardingData = prepareOnboardingOnyxData({
introSelected,
engagementChoice: choice,
onboardingMessage,
});
if (onboardingData) {
optimisticData.push(...onboardingData.optimisticData, {
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.NVP_INTRO_SELECTED,
value: {
isInviteOnboardingComplete: true,
},
});
successData.push(...onboardingData.successData);
failureData.push(...onboardingData.failureData);
parameters.guidedSetupData = JSON.stringify(onboardingData.guidedSetupData);
}
}
}
API.write(WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData});
}
- In
src/libs/Navigation/AppNavigator/AuthScreenslisten for the Concierge report ID:
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true});
- And then add this
useEffect:
useEffect(() => {
if (!conciergeReportID || !introSelected) {
return;
}
Report.openOnboardingDataOnce(conciergeReportID, introSelected);
}, [conciergeReportID, introSelected]);
What alternative solutions did you explore? (Optional)
We can also work with the BE to create a new API such as OPEN_ONBOARDING_DATA, however tasks are still supposed to be fully optimistic so I'm not sure this is the best idea.
Result:
Scenario 1:
https://github.com/user-attachments/assets/5ba2d6fc-efdb-4cf2-9755-982cd59e0f42
Scenario 2:
https://github.com/user-attachments/assets/4a75e62e-3195-4d8b-a474-af652e4e5159
@trjExpensify This is a BE issue as complete data is not sent via OpenApp.
It's because we're opening the app for the first time, not opening any reports, and going straight to the test drive via Reports tab. This means this line which makes sure we (optimistically) have the onboarding data (including the test drive task) never has the chance to execute:
@LorenzoBloedow are you saying that this only happens when..
- A user is invited to the workspace
- They receive an invite email (which links to their workspace chat)
- They don't click on that link
- They go to new.expensify.com natively
- They sign-in
- They go to Reports
- They click on the TestDrive and complete it
Whereas, if they followed this path below it would have auto-completed the task... ?
- A user is invited to the workspace
- They receive an invite email (which links to their workspace chat)
- They click on that link
- They get navigated to the workspace chat
- They go to the Reports page
- They click on the TestDrive and complete it
- The task auto-completes
@trjExpensify No, it happens when a user goes straight to the test drive without manually opening any reports.
It seems when we go through the second flow you described, we aren't calling openReport, so the same thing still happens even though the report technically opened (just probably not through openReport):
https://github.com/user-attachments/assets/6e50646f-57ab-4e7a-af6a-09dffac087d7
(Notice it does auto complete after I open the Concierge report and try again ^)
When we do manually open the report after leaving the one we landed on through the link, the task does auto-complete:
https://github.com/user-attachments/assets/0433940f-ee60-41ed-bd2b-a2d63dbdef61
So basically the task does not auto-complete in both cases you described.
It seems when we go through the second flow you described, we aren't calling openReport, so the same thing still happens even though the report technically opened (just probably not through openReport)
Super interesting...
Why wouldn't a deep link to a chat/report via a URL call OpenReport? π€ CC: @mountiny
Either it's not calling openReport or for some other reason not triggering this section of openReport:
https://github.com/Expensify/App/blob/2bc400a5b780d76de1a589bfe44163928c35fc6d/src/libs/actions/Report.ts#L1170-L1204
π£ It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? πΈ
yeah, but which one is it?
This is an invited workspace member, so maybe there's some clues looking at the workspace inviteType.
@trjExpensify Just confirmed it. In the deep link flow, openReport does execute, but introSelected is undefined: https://github.com/Expensify/App/blob/2bc400a5b780d76de1a589bfe44163928c35fc6d/src/libs/actions/Report.ts#L1170
Any idea why? Like, did the user account have introSelected.inviteType: workspace set on account creation when invited or not?
@trjExpensify Because OpenReport gets called before OpenApp in that flow:
I made a fix that makes it so that we have to wait for OpenApp before calling OpenReport, here's how it looks:
https://github.com/user-attachments/assets/a9f5d0b0-66e7-4a25-a34a-b3b3f97464da
@shubham1206agra 8 days overdue is a lot. Should this be a Weekly issue? If so, feel free to change it!
Mhm, something doesn't look right there. The skeleton report swipes left/right and going back you have to do twice.
@shubham1206agra can you weigh in here, please?
@trjExpensify Not sure what is happening here. Should we consult with someone from the expert team for assistance?
@mountiny reckon we can put it in #quality then? Doesn't seem like we can figure out why OpenReport is being called before OpenApp, and when Lorenzo does that we get this weird outcome: https://github.com/Expensify/App/issues/76714#issuecomment-3656261904
@trjExpensify We get the weird UI issue either way, take a look at the first and second video before that fix: https://github.com/Expensify/App/issues/76714#issuecomment-3636465326
So there's definitely something going on but I think it's 3 separate issues:
- This one (BE related).
- The other one we identified where we go through the deep link flow and
OpenReportgets called beforeOpenApp. - The weird UI flicker where the navigation animation happens twice through the deep link flow.
Added to quality as LOW, I think we can bump the price here as I feel like we should be able to have clearer RCA from external contributor on this one before we put hourly workers on this
Upwork job price has been updated to $500
Upwork job price has been updated to $500
@shubham1206agra Just updated my proposal, please let me know what you think.
@mountiny Would also like to know if you think the RCA is clearer now.
Thanks π
I am not sure - it still looks like OpenReport comes before OpenApp, that should not happen and that is fully FE issue
it still looks like OpenReport comes before OpenApp, that should not happen
Since there isn't a clear sequence of OpenApp > OpenReport it would make sense that something like this would happen when we use this promise:
https://github.com/Expensify/App/blob/2e4ad09929ca90236bf7765507ee1eea3aefdc5f/src/libs/actions/App.ts#L249-L265
To wait before calling openApp:
https://github.com/Expensify/App/blob/2e4ad09929ca90236bf7765507ee1eea3aefdc5f/src/libs/actions/App.ts#L363-L368
So I don't understand what the problem with this other approach is.
I can edit my proposal to combine its current decoupling with the "wait for openApp before calling openReport" approach.
I could implement it by forcing the SequentialQueue to put all OpenApp requests before any OpenReport, or simply waiting with a promise like I did before.
@mountiny Does that sound like something we'd want?
@trjExpensify @shubham1206agra this issue was created 2 weeks ago. Are we close to approving a proposal? If not, what's blocking us from getting this issue assigned? Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!
π¨ Edited by proposal-police: This proposal was edited at 2025-12-19 12:08:06 UTC.
Proposal
Please re-state the problem that we are trying to solve in this issue.
The concierge task is not updated after completing test drive task
What is the root cause of that problem?
The task is not completed because of:
https://github.com/Expensify/App/blob/d8f0f267fb43a24ac93c04e1dfd335c959a1ae91/src/components/TestDrive/TestDriveDemo.tsx#L49-L53
The viewTourTaskReport value is undefined. The data (taskName) is stored in nvp_introSelected.
For this user (invited to a workspace, using a narrow screen (i.e., a mobile phone), and logging in via magic code), nvp_introSelected is not updated correctly. It currently contains:
{
choice: 'newDotSubmit',
inviteType: 'workspace',
isInviteOnboardingComplete: false,
}
The task report data for the demo is not available because the onboarding flow is never displayed. As a result, the user does not complete onboarding, the CompleteGuidedSetup API call is never triggered, and nvp_introSelected is not updated.
The onboarding does not appear because the backend returns an empty object for nvp_onboarding. This is interpreted as onboarding being completed in the following locations:
https://github.com/Expensify/App/blob/bd763a95fdf9354fc83950b4af0a36fc0efd7326/src/selectors/Onboarding.ts#L13-L16
and in here:
https://github.com/Expensify/App/blob/bd763a95fdf9354fc83950b4af0a36fc0efd7326/src/libs/actions/Welcome/index.ts#L53-L56
So the onboarding flow won't show up
The nvp_introSelected is also updated when the user triggers OpenReport. That is why this issue does not occur on wide screens (the report screen is displayed, which triggers OpenReport).
For users on a narrow layout who log in via a workspace invitation link from email, there is an exitTo parameter that navigates the app to the workspace report after login:
https://github.com/Expensify/App/blob/6fe7e2768a94be21824ce76e8e711300f63f00e9/src/pages/ValidateLoginPage/index.tsx#L30
but because of the openReportFromDeepLink is called too early before signInWithValidateCode response arrive (which contains nvp_introSelected) this will make :
https://github.com/Expensify/App/blob/df5234bd3120bd81727c7e764f5af88dc878348d/src/libs/actions/Report.ts#L1163
intro selected is undefined and guidedSetupData is not embedded in the OpenReport request. So the nvp_introSelected is not updated properly.
What changes do you think we should make in order to solve the problem?
>) If the expected behavior is to display onboarding for this user, then we must also check the inviteType and isInviteOnboardingComplete fields in nvp_introSelected here:
https://github.com/Expensify/App/blob/bd763a95fdf9354fc83950b4af0a36fc0efd7326/src/selectors/Onboarding.ts#L13-L16
and in here:
https://github.com/Expensify/App/blob/bd763a95fdf9354fc83950b4af0a36fc0efd7326/src/libs/actions/Welcome/index.ts#L53-L56
Then after onboarding finish we navigate to the invited workspace chat
>) If the expected behavior is to open the workspace chat where the user was invited, we could call OpenReport of the workspace chat in isOnboardingFlowCompleted, or utilizing onServerDataReady of Welcome or in useOnboardingFlow or other place where we are sure the server data is ready, because we need the isInviteOnboardingComplete: false value in nvp_introSelected value for OpenReport to pass the GuidedSetupData to back end.
Result (if we want to open the invited workspace chat:)
https://github.com/user-attachments/assets/baca5f57-5913-4394-be7f-0e4fbabd1292
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.
CompleteGuidedSetupParams
@shubham1206agra can you please review the proposals π
@mountiny Do we need to show Onboarding flow for invited users?
I am not sure what is the structure around no actually, @danielrvidal @trjExpensify @Expensify/design might be able to help better with this one