diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index a0c8f81a4359..787b33bbb99e 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -8756,7 +8756,11 @@ function buildOptimisticResolvedDuplicatesReportAction(): OptimisticDismissedVio
};
}
-function buildOptimisticChangeApproverReportAction(managerID: number, actorAccountID: number): OptimisticChangedApproverReportAction {
+function buildOptimisticChangeApproverReportAction(
+ managerID: number,
+ actorAccountID: number,
+ formatPhoneNumber: LocaleContextProps['formatPhoneNumber'],
+): OptimisticChangedApproverReportAction {
const created = DateUtils.getDBTime();
return {
actionName: managerID === actorAccountID ? CONST.REPORT.ACTIONS.TYPE.TAKE_CONTROL : CONST.REPORT.ACTIONS.TYPE.REROUTE,
@@ -8766,7 +8770,7 @@ function buildOptimisticChangeApproverReportAction(managerID: number, actorAccou
message: [
{
type: CONST.REPORT.MESSAGE.TYPE.COMMENT,
- text: `changed the approver to ${getDisplayNameForParticipant({accountID: managerID, formatPhoneNumber: formatPhoneNumberPhoneUtils})}`,
+ text: `changed the approver to ${getDisplayNameForParticipant({accountID: managerID, formatPhoneNumber})}`,
html: `changed the approver to `,
},
],
diff --git a/src/libs/actions/IOU/ReportWorkflow.ts b/src/libs/actions/IOU/ReportWorkflow.ts
index fda76294ea2d..4ce049d684f6 100644
--- a/src/libs/actions/IOU/ReportWorkflow.ts
+++ b/src/libs/actions/IOU/ReportWorkflow.ts
@@ -1,3 +1,5 @@
+import type {LocaleContextProps} from '@components/LocaleContextProvider';
+
import * as API from '@libs/API';
import type {
AddReportApproverParams,
@@ -1731,8 +1733,9 @@ function assignReportToMe(
isASAPSubmitBetaEnabled: boolean,
reportCurrentNextStepDeprecated: OnyxEntry,
isTrackIntentUser: boolean | undefined,
+ formatPhoneNumber: LocaleContextProps['formatPhoneNumber'],
) {
- const takeControlReportAction = buildOptimisticChangeApproverReportAction(accountID, accountID);
+ const takeControlReportAction = buildOptimisticChangeApproverReportAction(accountID, accountID, formatPhoneNumber);
// buildOptimisticNextStep is used in parallel
const optimisticNextStepDeprecated = buildNextStepNew({
@@ -1838,19 +1841,55 @@ function assignReportToMe(
API.write(WRITE_COMMANDS.ASSIGN_REPORT_TO_ME, params, onyxData);
}
-function addReportApprover(
- report: OnyxTypes.Report,
- newApproverEmail: string,
- newApproverAccountID: number,
- accountID: number,
- email: string,
- policy: OnyxEntry,
- hasViolations: boolean,
- isASAPSubmitBetaEnabled: boolean,
- reportCurrentNextStepDeprecated: OnyxEntry,
- isTrackIntentUser: boolean | undefined,
-) {
- const takeControlReportAction = buildOptimisticChangeApproverReportAction(newApproverAccountID, accountID);
+type AddReportApproverOptions = {
+ /** Expense report whose approver is being changed. */
+ report: OnyxTypes.Report;
+
+ /** Email of the approver being added. */
+ newApproverEmail: string;
+
+ /** Account ID of the approver being added. */
+ newApproverAccountID: number;
+
+ /** Account ID of the user changing the approver. */
+ accountID: number;
+
+ /** Email of the user changing the approver. */
+ email: string;
+
+ /** Policy associated with the expense report. */
+ policy: OnyxEntry;
+
+ /** Whether the report currently has violations. */
+ hasViolations: boolean;
+
+ /** Whether ASAP Submit beta behavior is enabled. */
+ isASAPSubmitBetaEnabled: boolean;
+
+ /** Existing next-step data used to roll back optimistic updates on failure. */
+ reportCurrentNextStepDeprecated: OnyxEntry;
+
+ /** Whether the current user is in the track intent onboarding state. */
+ isTrackIntentUser: boolean | undefined;
+
+ /** Locale-aware formatter used for optimistic approver display names. */
+ formatPhoneNumber: LocaleContextProps['formatPhoneNumber'];
+};
+
+function addReportApprover({
+ report,
+ newApproverEmail,
+ newApproverAccountID,
+ accountID,
+ email,
+ policy,
+ hasViolations,
+ isASAPSubmitBetaEnabled,
+ reportCurrentNextStepDeprecated,
+ isTrackIntentUser,
+ formatPhoneNumber,
+}: AddReportApproverOptions) {
+ const takeControlReportAction = buildOptimisticChangeApproverReportAction(newApproverAccountID, accountID, formatPhoneNumber);
// buildOptimisticNextStep is used in parallel
const optimisticNextStepDeprecated = buildNextStepNew({
diff --git a/src/pages/DynamicReportChangeApproverPage.tsx b/src/pages/DynamicReportChangeApproverPage.tsx
index 866df5337ada..00283b354219 100644
--- a/src/pages/DynamicReportChangeApproverPage.tsx
+++ b/src/pages/DynamicReportChangeApproverPage.tsx
@@ -49,7 +49,7 @@ type DynamicReportChangeApproverPageProps = WithReportOrNotFoundProps & Platform
function DynamicReportChangeApproverPage({report, policy, isLoadingReportData}: DynamicReportChangeApproverPageProps) {
const reportID = report?.reportID;
- const {translate} = useLocalize();
+ const {translate, formatPhoneNumber} = useLocalize();
const styles = useThemeStyles();
const {environmentURL} = useEnvironment();
const currentUserDetails = useCurrentUserPersonalDetails();
@@ -89,9 +89,30 @@ function DynamicReportChangeApproverPage({report, policy, isLoadingReportData}:
Navigation.navigate(ROUTES.REPORT_CHANGE_APPROVER_ADD_APPROVER.getRoute(report.reportID));
return;
}
- assignReportToMe(report, currentUserDetails.accountID, currentUserDetails.email ?? '', policy, hasViolations, isASAPSubmitBetaEnabled, reportNextStep, isTrackIntentUser);
+ assignReportToMe(
+ report,
+ currentUserDetails.accountID,
+ currentUserDetails.email ?? '',
+ policy,
+ hasViolations,
+ isASAPSubmitBetaEnabled,
+ reportNextStep,
+ isTrackIntentUser,
+ formatPhoneNumber,
+ );
Navigation.dismissToPreviousRHP();
- }, [selectedApproverType, report, currentUserDetails.accountID, currentUserDetails.email, policy, hasViolations, isASAPSubmitBetaEnabled, reportNextStep, isTrackIntentUser]);
+ }, [
+ selectedApproverType,
+ report,
+ currentUserDetails.accountID,
+ currentUserDetails.email,
+ policy,
+ hasViolations,
+ isASAPSubmitBetaEnabled,
+ reportNextStep,
+ isTrackIntentUser,
+ formatPhoneNumber,
+ ]);
const approverTypes = useMemo(() => {
const data: Array> = [
diff --git a/src/pages/ReportAddApproverPage.tsx b/src/pages/ReportAddApproverPage.tsx
index d53219ba6663..26c4dee4bbe5 100644
--- a/src/pages/ReportAddApproverPage.tsx
+++ b/src/pages/ReportAddApproverPage.tsx
@@ -101,18 +101,19 @@ function ReportAddApproverPage({report, isLoadingReportData, policy}: ReportAddA
return;
}
startWithLoading(() => {
- addReportApprover(
+ addReportApprover({
report,
- selectedApproverEmail,
- Number(employeeAccountID),
- currentUserDetails.accountID,
- currentUserDetails.email ?? '',
+ newApproverEmail: selectedApproverEmail,
+ newApproverAccountID: Number(employeeAccountID),
+ accountID: currentUserDetails.accountID,
+ email: currentUserDetails.email ?? '',
policy,
hasViolations,
isASAPSubmitBetaEnabled,
- reportNextStep,
+ reportCurrentNextStepDeprecated: reportNextStep,
isTrackIntentUser,
- );
+ formatPhoneNumber,
+ });
Navigation.dismissToPreviousRHP();
});
};
diff --git a/src/pages/Search/SearchAddApproverPage.tsx b/src/pages/Search/SearchAddApproverPage.tsx
index 02261cfd94a1..bbcd0cf0e236 100644
--- a/src/pages/Search/SearchAddApproverPage.tsx
+++ b/src/pages/Search/SearchAddApproverPage.tsx
@@ -140,18 +140,19 @@ function SearchAddApproverPage() {
const hasViolations = hasViolationsReportUtils(report.reportID, transactionViolations, currentUserDetails.accountID, currentUserDetails.email ?? '');
const reportNextStep = allReportNextSteps?.[`${ONYXKEYS.COLLECTION.NEXT_STEP}${selectedReport.reportID}`];
- addReportApprover(
+ addReportApprover({
report,
- selectedApproverEmail,
- Number(employeeAccountID),
- currentUserDetails.accountID,
- currentUserDetails.email ?? '',
+ newApproverEmail: selectedApproverEmail,
+ newApproverAccountID: Number(employeeAccountID),
+ accountID: currentUserDetails.accountID,
+ email: currentUserDetails.email ?? '',
policy,
hasViolations,
isASAPSubmitBetaEnabled,
- reportNextStep,
+ reportCurrentNextStepDeprecated: reportNextStep,
isTrackIntentUser,
- );
+ formatPhoneNumber,
+ });
}
// Note: This clears both reports and transactions
diff --git a/src/pages/Search/SearchChangeApproverPage.tsx b/src/pages/Search/SearchChangeApproverPage.tsx
index 9a02523439cd..ec274094679e 100644
--- a/src/pages/Search/SearchChangeApproverPage.tsx
+++ b/src/pages/Search/SearchChangeApproverPage.tsx
@@ -73,7 +73,7 @@ function shouldAutoApplyApprover({
export {shouldAutoApplyApprover};
function SearchChangeApproverPage() {
- const {translate} = useLocalize();
+ const {translate, formatPhoneNumber} = useLocalize();
const styles = useThemeStyles();
const {environmentURL} = useEnvironment();
const currentUserDetails = useCurrentUserPersonalDetails();
@@ -175,7 +175,17 @@ function SearchChangeApproverPage() {
if (report.managerID !== currentUserDetails.accountID) {
const hasViolations = hasViolationsReportUtils(report.reportID, transactionViolations, currentUserDetails.accountID, currentUserDetails.email ?? '');
const reportNextStep = allReportNextSteps?.[`${ONYXKEYS.COLLECTION.NEXT_STEP}${selectedReport.reportID}`];
- assignReportToMe(report, currentUserDetails.accountID, currentUserDetails.email ?? '', policy, hasViolations, isASAPSubmitBetaEnabled, reportNextStep, isTrackIntentUser);
+ assignReportToMe(
+ report,
+ currentUserDetails.accountID,
+ currentUserDetails.email ?? '',
+ policy,
+ hasViolations,
+ isASAPSubmitBetaEnabled,
+ reportNextStep,
+ isTrackIntentUser,
+ formatPhoneNumber,
+ );
}
}
diff --git a/tests/actions/IOUTest/ReportWorkflowTest.ts b/tests/actions/IOUTest/ReportWorkflowTest.ts
index 8d8bb2131de4..e0564a9917a5 100644
--- a/tests/actions/IOUTest/ReportWorkflowTest.ts
+++ b/tests/actions/IOUTest/ReportWorkflowTest.ts
@@ -6,6 +6,7 @@ import OnyxListItemProvider from '@components/OnyxListItemProvider';
import useReportWithTransactionsAndViolations from '@hooks/useReportWithTransactionsAndViolations';
import {
+ addReportApprover,
approveMoneyRequest,
canApproveIOU,
canIOUBePaid,
@@ -53,7 +54,7 @@ import createRandomReportAction from '../../utils/collections/reportActions';
import {createRandomReport} from '../../utils/collections/reports';
import createRandomTransaction from '../../utils/collections/transaction';
import getOnyxValue from '../../utils/getOnyxValue';
-import {createGlobalFetchMock, getOnyxData, getRequiredOnyxUpdate, getRequiredOnyxUpdates, getRequiredWriteCall, localeCompare} from '../../utils/TestHelper';
+import {createGlobalFetchMock, formatPhoneNumber, getOnyxData, getRequiredOnyxUpdate, getRequiredOnyxUpdates, getRequiredWriteCall, localeCompare} from '../../utils/TestHelper';
import {isObject} from '../../utils/typeGuards';
import waitForBatchedUpdates from '../../utils/waitForBatchedUpdates';
import waitForBatchedUpdatesWithAct from '../../utils/waitForBatchedUpdatesWithAct';
@@ -3092,6 +3093,55 @@ describe('actions/IOU/ReportWorkflow', () => {
});
});
+ describe('change approver formatter forwarding', () => {
+ it('uses the injected formatter for the optimistic approver display name', async () => {
+ // eslint-disable-next-line rulesdir/no-multiple-api-calls -- Inspecting API.write optimistic data to verify formatter forwarding.
+ const apiWriteSpy = jest.spyOn(API, 'write').mockImplementation(() => Promise.resolve());
+ const approverAccountID = 8332403627;
+ const approverLogin = '+18332403627@expensify.sms';
+ const formatPhoneNumberSpy = jest.fn(formatPhoneNumber);
+ const policy = createRandomPolicy(1);
+ const report: Report = {
+ ...createRandomReport(1, undefined),
+ reportID: 'change-approver-report',
+ type: CONST.REPORT.TYPE.EXPENSE,
+ policyID: policy.id,
+ managerID: CARLOS_ACCOUNT_ID,
+ statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED,
+ };
+
+ await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {
+ [approverAccountID]: {
+ accountID: approverAccountID,
+ login: approverLogin,
+ isOptimisticPersonalDetail: true,
+ },
+ });
+ await waitForBatchedUpdates();
+
+ addReportApprover({
+ report,
+ newApproverEmail: approverLogin,
+ newApproverAccountID: approverAccountID,
+ accountID: RORY_ACCOUNT_ID,
+ email: RORY_EMAIL,
+ policy,
+ hasViolations: false,
+ isASAPSubmitBetaEnabled: false,
+ reportCurrentNextStepDeprecated: undefined,
+ isTrackIntentUser: false,
+ formatPhoneNumber: formatPhoneNumberSpy,
+ });
+
+ const [, , onyxData] = getRequiredWriteCall(apiWriteSpy.mock.calls, 0);
+ const reportActionsUpdate = getRequiredOnyxUpdate(onyxData, 'optimisticData', `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, Onyx.METHOD.MERGE, true);
+ const reportAction = getRequiredReportAction(reportActionsUpdate);
+
+ expect(formatPhoneNumberSpy).toHaveBeenCalledWith(approverLogin);
+ expect(reportAction.message).toEqual([expect.objectContaining({text: `changed the approver to ${formatPhoneNumber(approverLogin)}`})]);
+ });
+ });
+
describe('approveMoneyRequest Submit workspace upgrade', () => {
const submitPolicyID = 'submit-policy-id';
const teamPolicyID = 'team-policy-id';