From 816214820dc8d79681839a66510013f7c6a27102 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 24 Oct 2025 17:23:32 +0800 Subject: [PATCH 01/12] fix unavailable workspace is shown when share track expense with accountant --- src/libs/OptionsListUtils/index.ts | 4 ++-- src/libs/ReportUtils.ts | 6 +++--- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index a85c4e88bc8e..a5520709043c 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -941,8 +941,8 @@ function createOption( /** * Get the option for a given report. */ -function getReportOption(participant: Participant, reportAttributesDerived?: ReportAttributesDerivedValue['reports']): OptionData { - const report = getReportOrDraftReport(participant.reportID); +function getReportOption(participant: Participant, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], reportDrafts?: OnyxCollection): OptionData { + const report = getReportOrDraftReport(participant.reportID, undefined, undefined, reportDrafts); const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); const option = createOption( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9fcc787e3459..1946c04f43af 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1191,10 +1191,10 @@ function getChatType(report: OnyxInputOrEntry | Participant): ValueOf | SearchReport { +function getReportOrDraftReport(reportID: string | undefined, searchReports?: SearchReport[], fallbackReport?: Report, reportDrafts?: OnyxCollection): OnyxEntry | SearchReport { const searchReport = searchReports?.find((report) => report.reportID === reportID); const onyxReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; - return searchReport ?? onyxReport ?? allReportsDraft?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`] ?? fallbackReport; + return searchReport ?? onyxReport ?? (reportDrafts ?? allReportsDraft)?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`] ?? fallbackReport; } function reportTransactionsSelector(transactions: OnyxCollection, reportID: string | undefined): Transaction[] { @@ -6511,7 +6511,7 @@ function buildOptimisticExpenseReport( // The amount for Expense reports are stored as negative value in the database const storedTotal = total * -1; const storedNonReimbursableTotal = nonReimbursableTotal * -1; - const report = chatReportID ? getReport(chatReportID, allReports) : undefined; + const report = chatReportID ? getReportOrDraftReport(chatReportID) : undefined; const policyName = getPolicyName({report}); const formattedTotal = convertToDisplayString(storedTotal, currency); // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 3f2f94dea373..1ebdb8073da8 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -158,6 +158,7 @@ function IOURequestStepConfirmation({ const [reportAttributesDerived] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {canBeMissing: true, selector: reportsSelector}); const [recentlyUsedDestinations] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_DESTINATIONS}${realPolicyID}`, {canBeMissing: true}); const [policyRecentlyUsedCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${realPolicyID}`, {canBeMissing: true}); + const [reportDrafts] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT, {canBeMissing: true}); /* * We want to use a report from the transaction if it exists @@ -244,9 +245,9 @@ function IOURequestStepConfirmation({ if (participant.isSender && iouType === CONST.IOU.TYPE.INVOICE) { return participant; } - return participant.accountID ? getParticipantsOption(participant, personalDetails) : getReportOption(participant, reportAttributesDerived); + return participant.accountID ? getParticipantsOption(participant, personalDetails) : getReportOption(participant, reportAttributesDerived, reportDrafts); }) ?? [], - [transaction?.participants, iouType, personalDetails, reportAttributesDerived], + [transaction?.participants, iouType, personalDetails, reportAttributesDerived, reportDrafts], ); const isPolicyExpenseChat = useMemo(() => participants?.some((participant) => participant.isPolicyExpenseChat), [participants]); const shouldGenerateTransactionThreadReport = !isBetaEnabled(CONST.BETAS.NO_OPTIMISTIC_TRANSACTION_THREADS); From 09ad23b2b685a7b410f2bbdc78a808cefcdf4184 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 24 Oct 2025 17:35:53 +0800 Subject: [PATCH 02/12] prettier --- src/libs/ReportUtils.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1946c04f43af..365e3a1aff8f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1191,7 +1191,12 @@ function getChatType(report: OnyxInputOrEntry | Participant): ValueOf): OnyxEntry | SearchReport { +function getReportOrDraftReport( + reportID: string | undefined, + searchReports?: SearchReport[], + fallbackReport?: Report, + reportDrafts?: OnyxCollection, +): OnyxEntry | SearchReport { const searchReport = searchReports?.find((report) => report.reportID === reportID); const onyxReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; return searchReport ?? onyxReport ?? (reportDrafts ?? allReportsDraft)?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`] ?? fallbackReport; From 97ec995520ed5f8c7ecbd86ca13ffd89092e4c26 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 29 Oct 2025 13:00:57 +0800 Subject: [PATCH 03/12] add test --- tests/unit/ReportUtilsTest.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 0fb74eb5d8c8..930fbcf4ba7c 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -7,6 +7,7 @@ import Onyx from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; import {putOnHold} from '@libs/actions/IOU'; import type {OnboardingTaskLinks} from '@libs/actions/Welcome/OnboardingFlow'; +import {convertToDisplayString} from '@libs/CurrencyUtils'; import DateUtils from '@libs/DateUtils'; import {getEnvironmentURL} from '@libs/Environment/Environment'; import getBase62ReportID from '@libs/getBase62ReportID'; @@ -8519,4 +8520,25 @@ describe('ReportUtils', () => { expect(result).toEqual(mockOnyxReport); }); }); + + describe('buildOptimisticExpenseReport', () => { + it('should include the policy name in report name from report draft', async () => { + const chatReportID = '1'; + const policyID = '2'; + const reportDraft: Report = { + ...createRandomReport(Number(chatReportID)), + policyID, + }; + const policy: Policy = { + ...createRandomPolicy(Number(policyID)), + }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${chatReportID}`, reportDraft); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, policy); + + const total = 100; + const currency = CONST.CURRENCY.USD; + const expenseReport = buildOptimisticExpenseReport(chatReportID, undefined, 1, total, currency); + expect(expenseReport.reportName).toBe(`${policy.name} owes ${convertToDisplayString(-total, currency)}`); + }); + }); }); From 38c65b36ebf0f13c238ef66a5a16862df3148cc1 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sun, 2 Nov 2025 15:49:07 +0800 Subject: [PATCH 04/12] lint --- tests/unit/ReportUtilsTest.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index e1c6e37322e0..a636b24b0b81 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -8435,16 +8435,14 @@ describe('ReportUtils', () => { ...createRandomReport(Number(chatReportID), CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT), policyID, }; - const policy: Policy = { - ...createRandomPolicy(Number(policyID)), - }; + const fakePolicy: Policy = createRandomPolicy(Number(policyID)); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${chatReportID}`, reportDraft); - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, policy); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); const total = 100; const currency = CONST.CURRENCY.USD; const expenseReport = buildOptimisticExpenseReport(chatReportID, undefined, 1, total, currency); - expect(expenseReport.reportName).toBe(`${policy.name} owes ${convertToDisplayString(-total, currency)}`); + expect(expenseReport.reportName).toBe(`${fakePolicy.name} owes ${convertToDisplayString(-total, currency)}`); }); }); From f2a7763564bc92113afcdac1607c307c6d3dea2d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 3 Nov 2025 11:56:15 +0800 Subject: [PATCH 05/12] fix test --- tests/unit/ReportUtilsTest.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index a636b24b0b81..2fc5782be525 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -8428,6 +8428,8 @@ describe('ReportUtils', () => { }); describe('buildOptimisticExpenseReport', () => { + beforeEach(async () => await Onyx.clear()); + it('should include the policy name in report name from report draft', async () => { const chatReportID = '1'; const policyID = '2'; From 0f427cc0d8407ebe11102dfce282c6375131d298 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 3 Nov 2025 12:08:50 +0800 Subject: [PATCH 06/12] lint --- tests/unit/ReportUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 2fc5782be525..9d9120342cc4 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -8428,7 +8428,7 @@ describe('ReportUtils', () => { }); describe('buildOptimisticExpenseReport', () => { - beforeEach(async () => await Onyx.clear()); + beforeEach(Onyx.clear); it('should include the policy name in report name from report draft', async () => { const chatReportID = '1'; From 4de4ff460e40813603c1633fed25c0e8676285e6 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 3 Nov 2025 13:26:44 +0800 Subject: [PATCH 07/12] fix expense report optimistic name --- src/libs/ReportUtils.ts | 21 ++++++++++++++++----- src/libs/actions/Policy/Policy.ts | 11 +++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e386584a84e5..c7223a6898d5 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -993,6 +993,13 @@ Onyx.connect({ callback: (value) => (allPolicies = value), }); +let allPolicyDrafts: OnyxCollection; +Onyx.connectWithoutView({ + key: ONYXKEYS.COLLECTION.POLICY_DRAFTS, + waitForCollectionCallback: true, + callback: (value) => (allPolicyDrafts = value), +}); + let allReports: OnyxCollection; let reportsByPolicyID: ReportByPolicyMap; Onyx.connect({ @@ -1332,6 +1339,7 @@ function getPolicyName({report, returnEmptyIfNotFound = false, policy, policies, const noPolicyFound = returnEmptyIfNotFound ? '' : unavailableTranslation; const parentReport = report ? getRootParentReport({report, reports}) : undefined; + console.log('empty?', policies, allPolicies) if (isEmptyObject(report) || (isEmptyObject(policies) && isEmptyObject(allPolicies) && !report?.policyName && !parentReport?.policyName)) { return noPolicyFound; } @@ -4182,10 +4190,11 @@ function getReportFieldsByPolicyID(policyID: string | undefined): Record key.replace(ONYXKEYS.COLLECTION.POLICY, '') === policyID); - const fieldList = policyReportFields?.[1]?.fieldList; + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];; + const policyDraft = allPolicyDrafts?.[`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`]; + const fieldList = (policy ?? policyDraft)?.fieldList; - if (!policyReportFields || !fieldList) { + if ((!policy && !policyDraft) || !fieldList) { return {}; } @@ -6568,7 +6577,9 @@ function buildOptimisticExpenseReport( const formattedTotal = convertToDisplayString(storedTotal, currency); // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); + const policyReal = getPolicy(policyID); + const policyDraft = allPolicyDrafts?.[`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`]; + const policy = policyReal ?? policyDraft; const {stateNum, statusNum} = getExpenseReportStateAndStatus(policy); @@ -6604,7 +6615,7 @@ function buildOptimisticExpenseReport( } const titleReportField = getTitleReportField(getReportFieldsByPolicyID(policyID) ?? {}); - if (!!titleReportField && isPaidGroupPolicyExpenseReport(expenseReport)) { + if (!!titleReportField && isGroupPolicy(policy?.type ?? '')) { expenseReport.reportName = populateOptimisticReportFormula(titleReportField.defaultValue, expenseReport, policy); } diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 6d3b80ece144..11ea7d850ce4 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -2563,6 +2563,17 @@ function createDraftWorkspace( disabledFields: {defaultBillable: true, reimbursable: false}, requiresCategory: true, defaultReimbursable: true, + 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, + }, + }, }, }, { From 43e371d39895defc20c1a9ea9aa83696c2a58e23 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 3 Nov 2025 13:27:31 +0800 Subject: [PATCH 08/12] remove log --- src/libs/ReportUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c7223a6898d5..06bc870b1130 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1339,7 +1339,6 @@ function getPolicyName({report, returnEmptyIfNotFound = false, policy, policies, const noPolicyFound = returnEmptyIfNotFound ? '' : unavailableTranslation; const parentReport = report ? getRootParentReport({report, reports}) : undefined; - console.log('empty?', policies, allPolicies) if (isEmptyObject(report) || (isEmptyObject(policies) && isEmptyObject(allPolicies) && !report?.policyName && !parentReport?.policyName)) { return noPolicyFound; } From 89d5b5edeb9b25d2a05169e251d3760b0fb615a2 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 3 Nov 2025 13:34:02 +0800 Subject: [PATCH 09/12] prettier --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 06bc870b1130..d6553ea7fad9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4189,7 +4189,7 @@ function getReportFieldsByPolicyID(policyID: string | undefined): Record Date: Fri, 7 Nov 2025 06:18:30 +0800 Subject: [PATCH 10/12] added lint ignore back --- src/libs/ReportUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2637e61b03cf..33cefbf4a453 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1193,6 +1193,7 @@ function getChatType(report: OnyxInputOrEntry | Participant): ValueOf Date: Fri, 7 Nov 2025 16:38:08 +0800 Subject: [PATCH 11/12] suppress lint --- src/libs/ReportUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 33cefbf4a453..8f825100b032 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1193,12 +1193,13 @@ function getChatType(report: OnyxInputOrEntry | Participant): ValueOf, + // eslint-disable-next-line @typescript-eslint/no-deprecated ): OnyxEntry | SearchReport { const searchReport = searchReports?.find((report) => report.reportID === reportID); const onyxReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; From 13a73a73b676cbff15dd3fed617714300d05d6f4 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 8 Nov 2025 12:33:09 +0800 Subject: [PATCH 12/12] prettier --- src/libs/ReportUtils.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a64dcd1dc741..fc18b6a23ef8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1215,12 +1215,7 @@ function getChatType(report: OnyxInputOrEntry | Participant): ValueOf, -): OnyxEntry { +function getReportOrDraftReport(reportID: string | undefined, searchReports?: Report[], fallbackReport?: Report, reportDrafts?: OnyxCollection): OnyxEntry { const searchReport = searchReports?.find((report) => report.reportID === reportID); const onyxReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; return searchReport ?? onyxReport ?? (reportDrafts ?? allReportsDraft)?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`] ?? fallbackReport;