From 36bc7209f56f148f957278def8b1c6ec42df0ccd Mon Sep 17 00:00:00 2001 From: Maruf Sharifi Date: Sat, 18 Jul 2026 10:25:23 +0300 Subject: [PATCH 1/2] Thread localized phone formatter through invoice actions --- src/libs/actions/IOU/SendInvoice.ts | 7 ++++++- .../request/step/IOURequestStepCompanyInfo.tsx | 3 ++- .../step/confirmation/useExpenseSubmission.ts | 3 ++- tests/actions/IOUTest/SendInvoiceTest.ts | 17 ++++++++++++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU/SendInvoice.ts b/src/libs/actions/IOU/SendInvoice.ts index b78e32a89051..37755f40cce3 100644 --- a/src/libs/actions/IOU/SendInvoice.ts +++ b/src/libs/actions/IOU/SendInvoice.ts @@ -1,10 +1,11 @@ +import type {LocaleContextProps} from '@components/LocaleContextProvider'; + import * as API from '@libs/API'; import type {SendInvoiceParams} from '@libs/API/parameters'; import {WRITE_COMMANDS} from '@libs/API/types'; import DateUtils from '@libs/DateUtils'; import {deferOrExecuteWrite} from '@libs/deferredLayoutWrite'; import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils'; -import {formatPhoneNumber} from '@libs/LocalePhoneNumber'; import Log from '@libs/Log'; import {getReportActionHtml, getReportActionText} from '@libs/ReportActionsUtils'; import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction} from '@libs/ReportUtils'; @@ -85,6 +86,7 @@ type SendInvoiceOptions = { policyRecentlyUsedTags?: OnyxEntry; isFromGlobalCreate?: boolean; senderPolicyTags: OnyxEntry; + formatPhoneNumber: LocaleContextProps['formatPhoneNumber']; // TODO: delegateAccountID will be made required in PR 12 when all callers pass the value (https://github.com/Expensify/App/issues/66425) delegateAccountID?: number | undefined; }; @@ -590,6 +592,7 @@ function getSendInvoiceInformation({ policyRecentlyUsedCategories, policyRecentlyUsedTags, senderPolicyTags, + formatPhoneNumber, delegateAccountID, }: SendInvoiceOptions): SendInvoiceInformation { const {amount = 0, currency = '', created = '', merchant = '', category = '', tag = '', taxCode = '', taxAmount = 0, taxValue, billable, comment, participants} = transaction ?? {}; @@ -739,6 +742,7 @@ function sendInvoice({ policyRecentlyUsedTags, isFromGlobalCreate = false, senderPolicyTags, + formatPhoneNumber, delegateAccountID, }: SendInvoiceOptions) { const parsedComment = getParsedComment(transaction?.comment?.comment?.trim() ?? ''); @@ -774,6 +778,7 @@ function sendInvoice({ policyRecentlyUsedCategories, policyRecentlyUsedTags, senderPolicyTags: senderPolicyTags ?? {}, + formatPhoneNumber, delegateAccountID, }); diff --git a/src/pages/iou/request/step/IOURequestStepCompanyInfo.tsx b/src/pages/iou/request/step/IOURequestStepCompanyInfo.tsx index 42763de1717e..4d63772a322b 100644 --- a/src/pages/iou/request/step/IOURequestStepCompanyInfo.tsx +++ b/src/pages/iou/request/step/IOURequestStepCompanyInfo.tsx @@ -48,7 +48,7 @@ function IOURequestStepCompanyInfo({route, report, transaction}: IOURequestStepC const {backTo, reportID} = route.params; const styles = useThemeStyles(); - const {translate} = useLocalize(); + const {translate, formatPhoneNumber} = useLocalize(); const {convertToDisplayString} = useCurrencyListActions(); const {inputCallbackRef} = useAutoFocusInput(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); @@ -120,6 +120,7 @@ function IOURequestStepCompanyInfo({route, report, transaction}: IOURequestStepC policyRecentlyUsedTags, isFromGlobalCreate, senderPolicyTags: policyTags ?? {}, + formatPhoneNumber, }); cleanupAndNavigateAfterExpenseCreate({ report: undefined, diff --git a/src/pages/iou/request/step/confirmation/useExpenseSubmission.ts b/src/pages/iou/request/step/confirmation/useExpenseSubmission.ts index 732a40a2dd7a..15f49d0657e5 100644 --- a/src/pages/iou/request/step/confirmation/useExpenseSubmission.ts +++ b/src/pages/iou/request/step/confirmation/useExpenseSubmission.ts @@ -196,7 +196,7 @@ function useExpenseSubmission(params: UseExpenseSubmissionParams) { } = params; // Localization - const {translate, toLocaleDigit} = useLocalize(); + const {translate, toLocaleDigit, formatPhoneNumber} = useLocalize(); const delegateAccountID = useDelegateAccountID(); // Permissions @@ -1019,6 +1019,7 @@ function useExpenseSubmission(params: UseExpenseSubmissionParams) { isFromGlobalCreate: getIsFromGlobalCreate(transaction), policyRecentlyUsedTags, senderPolicyTags: senderWorkspacePolicyTags ?? {}, + formatPhoneNumber, }); if (shouldHandleNavigation) { cleanupAndNavigateAfterExpenseCreate({ diff --git a/tests/actions/IOUTest/SendInvoiceTest.ts b/tests/actions/IOUTest/SendInvoiceTest.ts index 8683ed262b2b..43a40eede49d 100644 --- a/tests/actions/IOUTest/SendInvoiceTest.ts +++ b/tests/actions/IOUTest/SendInvoiceTest.ts @@ -27,7 +27,7 @@ import createRandomPolicy from '../../utils/collections/policies'; import createRandomTransaction from '../../utils/collections/transaction'; import getOnyxValue from '../../utils/getOnyxValue'; import initCurrencyListContext from '../../utils/initCurrencyListContext'; -import {getGlobalFetchMock} from '../../utils/TestHelper'; +import {formatPhoneNumber, getGlobalFetchMock} from '../../utils/TestHelper'; import waitForBatchedUpdates from '../../utils/waitForBatchedUpdates'; const topMostReportID = '23423423'; @@ -172,6 +172,7 @@ describe('actions/SendInvoice', () => { companyWebsite: undefined, policyRecentlyUsedCategories: existingRecentlyUsedCategories, senderPolicyTags: baseSenderPolicyTags, + formatPhoneNumber, }); // Then: Verify optimistic data is generated when policyRecentlyUsedCategories are provided @@ -197,6 +198,7 @@ describe('actions/SendInvoice', () => { companyWebsite: undefined, policyRecentlyUsedCategories: undefined, senderPolicyTags: baseSenderPolicyTags, + formatPhoneNumber, }); expect(result.onyxData.optimisticData).toBeDefined(); @@ -248,6 +250,7 @@ describe('actions/SendInvoice', () => { companyWebsite: 'https://testcompany.com', policyRecentlyUsedCategories: ['Services', 'Consulting'], senderPolicyTags: mockPolicyTagList as PolicyTagLists, + formatPhoneNumber, }); // Then: Verify the result structure and key values @@ -325,6 +328,7 @@ describe('actions/SendInvoice', () => { companyWebsite: 'https://clientcompany.com', policyRecentlyUsedCategories: [], senderPolicyTags: baseSenderPolicyTags, + formatPhoneNumber, }); // Then: Verify the result uses existing chat report @@ -361,6 +365,7 @@ describe('actions/SendInvoice', () => { companyWebsite: undefined, policyRecentlyUsedCategories: [], senderPolicyTags: baseSenderPolicyTags, + formatPhoneNumber, }); // Then: Verify receipt handling @@ -410,6 +415,7 @@ describe('actions/SendInvoice', () => { companyWebsite: undefined, policyRecentlyUsedCategories: [], senderPolicyTags: baseSenderPolicyTags, + formatPhoneNumber, }); // Then: Verify function handles missing data gracefully @@ -440,6 +446,7 @@ describe('actions/SendInvoice', () => { companyWebsite: undefined, policyRecentlyUsedCategories: [], senderPolicyTags: baseSenderPolicyTags, + formatPhoneNumber, }); expect(result.invoiceRoom).toBeDefined(); @@ -492,6 +499,7 @@ describe('actions/SendInvoice', () => { companyWebsite: undefined, policyRecentlyUsedCategories: [], senderPolicyTags: baseSenderPolicyTags, + formatPhoneNumber, }); expect(result.invoiceRoom).toBeDefined(); @@ -545,6 +553,7 @@ describe('actions/SendInvoice', () => { policyRecentlyUsedCurrencies: [], policyRecentlyUsedTags, senderPolicyTags: senderPolicyTags ?? {}, + formatPhoneNumber, }); // Then: optimisticData should contain a POLICY_RECENTLY_USED_TAGS update with the transaction tag prepended @@ -592,6 +601,7 @@ describe('actions/SendInvoice', () => { currentUserAccountID: 123, policyRecentlyUsedCurrencies: [], senderPolicyTags: senderPolicyTags ?? {}, + formatPhoneNumber, }); // Then: No POLICY_RECENTLY_USED_TAGS update should be in optimisticData @@ -624,6 +634,7 @@ describe('actions/SendInvoice', () => { companyName, companyWebsite, senderPolicyTags: undefined, + formatPhoneNumber, }); // Then a new invoice chat is created instead of incorrectly using the invoice chat which has been converted from individual to business @@ -652,6 +663,7 @@ describe('actions/SendInvoice', () => { transaction, policyRecentlyUsedCurrencies: initialCurrencies, senderPolicyTags: undefined, + formatPhoneNumber, }); mockFetch?.fail?.(); @@ -692,6 +704,7 @@ describe('actions/SendInvoice', () => { policyRecentlyUsedCurrencies: [], policyRecentlyUsedCategories, senderPolicyTags: undefined, + formatPhoneNumber, }); // Then onyxData should be passed to API.write @@ -733,6 +746,7 @@ describe('actions/SendInvoice', () => { orderWeight: 0, }, }, + formatPhoneNumber, }); await waitForBatchedUpdates(); @@ -768,6 +782,7 @@ describe('actions/SendInvoice', () => { policyRecentlyUsedCurrencies: [], invoiceChatReportID: preGeneratedReportID, senderPolicyTags: undefined, + formatPhoneNumber, }); expect(writeSpy).toHaveBeenCalledWith( From fd7a3fc1a40b501d6cc0b557c42f6cc689807c47 Mon Sep 17 00:00:00 2001 From: Maruf Sharifi Date: Tue, 21 Jul 2026 18:50:37 +0300 Subject: [PATCH 2/2] Fixed type failure --- tests/actions/IOUTest/SendInvoiceTest.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/actions/IOUTest/SendInvoiceTest.ts b/tests/actions/IOUTest/SendInvoiceTest.ts index c2b900314813..ec9decad80a4 100644 --- a/tests/actions/IOUTest/SendInvoiceTest.ts +++ b/tests/actions/IOUTest/SendInvoiceTest.ts @@ -307,6 +307,7 @@ describe('actions/SendInvoice', () => { companyWebsite: 'https://testcompany.com', policyRecentlyUsedCategories: [], senderPolicyTags: baseSenderPolicyTags, + formatPhoneNumber, delegateAccountID: DELEGATE_ACCOUNT_ID, });