From 5a58af9dfd5caf7a3ffa112d88059c067cd69034 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Tue, 9 Sep 2025 21:16:28 +0100 Subject: [PATCH 01/50] add setWorkspaceAutoHarvesting command --- .../SetWorkspaceAutoHarvestingParams.ts | 6 ++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 + src/libs/actions/Policy/Policy.ts | 63 +++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 src/libs/API/parameters/SetWorkspaceAutoHarvestingParams.ts diff --git a/src/libs/API/parameters/SetWorkspaceAutoHarvestingParams.ts b/src/libs/API/parameters/SetWorkspaceAutoHarvestingParams.ts new file mode 100644 index 0000000000000..4a8baa1fd69a7 --- /dev/null +++ b/src/libs/API/parameters/SetWorkspaceAutoHarvestingParams.ts @@ -0,0 +1,6 @@ +type SetWorkspaceAutoHarvestingParams = { + policyID: string; + enabled: boolean; +}; + +export default SetWorkspaceAutoHarvestingParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 2c99cc64b325d..1cc001abaf115 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -196,6 +196,7 @@ export type {default as SetWorkspaceRequiresCategoryParams} from './SetWorkspace export type {default as DeleteWorkspaceCategoriesParams} from './DeleteWorkspaceCategoriesParams'; export type {default as UpdatePolicyCategoryPayrollCodeParams} from './UpdatePolicyCategoryPayrollCodeParams'; export type {default as UpdatePolicyCategoryGLCodeParams} from './UpdatePolicyCategoryGLCodeParams'; +export type {default as SetWorkspaceAutoHarvestingParams} from './SetWorkspaceAutoHarvestingParams'; export type {default as SetWorkspaceAutoReportingFrequencyParams} from './SetWorkspaceAutoReportingFrequencyParams'; export type {default as SetWorkspaceAutoReportingMonthlyOffsetParams} from './SetWorkspaceAutoReportingMonthlyOffsetParams'; export type {default as SetWorkspaceApprovalModeParams} from './SetWorkspaceApprovalModeParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index e0e8183631be2..30ff187527fff 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -13,6 +13,7 @@ type ApiRequestType = ValueOf; const WRITE_COMMANDS = { CLEAN_POLICY_TAGS: 'ClearPolicyTags', IMPORT_MULTI_LEVEL_TAGS: 'ImportMultiLevelTags', + SET_WORKSPACE_AUTO_HARVESTING: 'SetWorkspaceAutoHarvesting', SET_WORKSPACE_AUTO_REPORTING_FREQUENCY: 'SetWorkspaceAutoReportingFrequency', SET_WORKSPACE_AUTO_REPORTING_MONTHLY_OFFSET: 'SetWorkspaceAutoReportingOffset', SET_WORKSPACE_APPROVAL_MODE: 'SetWorkspaceApprovalMode', @@ -721,6 +722,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.CANCEL_PAYMENT]: Parameters.CancelPaymentParams; [WRITE_COMMANDS.ACCEPT_ACH_CONTRACT_FOR_BANK_ACCOUNT]: Parameters.AcceptACHContractForBankAccount; [WRITE_COMMANDS.UPDATE_WORKSPACE_DESCRIPTION]: Parameters.UpdateWorkspaceDescriptionParams; + [WRITE_COMMANDS.SET_WORKSPACE_AUTO_HARVESTING]: Parameters.SetWorkspaceAutoHarvestingParams; [WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING_FREQUENCY]: Parameters.SetWorkspaceAutoReportingFrequencyParams; [WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING_MONTHLY_OFFSET]: Parameters.SetWorkspaceAutoReportingMonthlyOffsetParams; [WRITE_COMMANDS.SET_WORKSPACE_APPROVAL_MODE]: Parameters.SetWorkspaceApprovalModeParams; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 945a5e3cadaf3..24a30aff68c8a 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -51,6 +51,7 @@ import type { SetPolicyProhibitedExpensesParams, SetPolicyRulesEnabledParams, SetWorkspaceApprovalModeParams, + SetWorkspaceAutoHarvestingParams, SetWorkspaceAutoReportingFrequencyParams, SetWorkspaceAutoReportingMonthlyOffsetParams, SetWorkspacePayerParams, @@ -575,6 +576,67 @@ function deleteWorkspace(policyID: string, policyName: string, lastAccessedWorks } } +/* Set the auto harvesting on a workspace. This goes in tandem with auto reporting. so when you enable/disable + * harvesting, you are enabling/disabling auto reporting too. + */ +function setWorkspaceAutoHarvesting(policy: Policy, enabled: boolean) { + const policyID = policy.id; + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + autoReporting: enabled, + harvesting: {enabled}, + pendingFields: { + autoReporting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + harvesting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + autoReporting: null, + harvesting: null, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + autoReporting: policy?.autoReporting ?? null, + harvesting: policy?.harvesting ?? null, + pendingFields: { + autoReporting: null, + harvesting: null, + }, + errorFields: { + autoReporting: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workflowsDelayedSubmissionPage.autoReportingErrorMessage'), + harvesting: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + autoReporting: null, + harvesting: null, + }, + }, + }, + ]; + + const params: SetWorkspaceAutoHarvestingParams = {policyID, enabled}; + API.write(WRITE_COMMANDS.SET_WORKSPACE_AUTO_HARVESTING, params, {optimisticData, failureData, successData}); +} + function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf) { // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 // eslint-disable-next-line deprecation/deprecation @@ -6250,6 +6312,7 @@ export { buildOptimisticRecentlyUsedCurrencies, setWorkspaceInviteMessageDraft, setWorkspaceApprovalMode, + setWorkspaceAutoHarvesting, setWorkspaceAutoReportingFrequency, setWorkspaceAutoReportingMonthlyOffset, updateWorkspaceDescription, From 596d9eaaef901c4581576c8682226ccf4f14721e Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Tue, 9 Sep 2025 21:56:31 +0100 Subject: [PATCH 02/50] update policy paramter type --- src/libs/actions/Policy/Policy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 24a30aff68c8a..e91c4dcead74c 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -579,8 +579,8 @@ function deleteWorkspace(policyID: string, policyName: string, lastAccessedWorks /* Set the auto harvesting on a workspace. This goes in tandem with auto reporting. so when you enable/disable * harvesting, you are enabling/disabling auto reporting too. */ -function setWorkspaceAutoHarvesting(policy: Policy, enabled: boolean) { - const policyID = policy.id; +function setWorkspaceAutoHarvesting(policy: OnyxEntry, enabled: boolean) { + const policyID = policy?.id; const optimisticData: OnyxUpdate[] = [ { From 41d097eae04e277f85e042a4b7cc25206c50cb5a Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Tue, 9 Sep 2025 22:40:43 +0100 Subject: [PATCH 03/50] call setWorkspaceAutoHarvesting on toggling delyed submission --- src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index c1b0b21b999ce..da8633f5192fd 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -29,6 +29,7 @@ import { openPolicyWorkflowsPage, setIsForcedToChangeCurrency, setWorkspaceApprovalMode, + setWorkspaceAutoHarvesting, setWorkspaceAutoReportingFrequency, setWorkspaceReimbursement, updateGeneralSettings, @@ -171,9 +172,7 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { title: translate('workflowsPage.delaySubmissionTitle'), subtitle: translate('workflowsPage.delaySubmissionDescription'), switchAccessibilityLabel: translate('workflowsPage.delaySubmissionDescription'), - onToggle: (isEnabled: boolean) => { - setWorkspaceAutoReportingFrequency(route.params.policyID, isEnabled ? CONST.POLICY.AUTO_REPORTING_FREQUENCIES.WEEKLY : CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT); - }, + onToggle: (isEnabled: boolean) => setWorkspaceAutoHarvesting(policy, isEnabled), subMenuItems: ( ), - isActive: (policy?.autoReportingFrequency !== CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT && !hasDelayedSubmissionError) ?? false, + isActive: (policy?.autoReporting && !hasDelayedSubmissionError) ?? false, pendingAction: policy?.pendingFields?.autoReporting ?? policy?.pendingFields?.autoReportingFrequency, errors: getLatestErrorField(policy ?? {}, CONST.POLICY.COLLECTION_KEYS.AUTOREPORTING), onCloseError: () => clearPolicyErrorField(route.params.policyID, CONST.POLICY.COLLECTION_KEYS.AUTOREPORTING), From 8162c2e71bd3ab52fd4381c2150d61f6678caf81 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Tue, 9 Sep 2025 22:42:24 +0100 Subject: [PATCH 04/50] set pending field on autoReporting only --- src/libs/actions/Policy/Policy.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index e91c4dcead74c..c9fd59fb1027f 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -591,11 +591,9 @@ function setWorkspaceAutoHarvesting(policy: OnyxEntry, enabled: boolean) harvesting: {enabled}, pendingFields: { autoReporting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - harvesting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, }, errorFields: { autoReporting: null, - harvesting: null, }, }, }, @@ -610,11 +608,9 @@ function setWorkspaceAutoHarvesting(policy: OnyxEntry, enabled: boolean) harvesting: policy?.harvesting ?? null, pendingFields: { autoReporting: null, - harvesting: null, }, errorFields: { autoReporting: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workflowsDelayedSubmissionPage.autoReportingErrorMessage'), - harvesting: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), }, }, }, @@ -627,7 +623,6 @@ function setWorkspaceAutoHarvesting(policy: OnyxEntry, enabled: boolean) value: { pendingFields: { autoReporting: null, - harvesting: null, }, }, }, From cb47073b1164d8bc1822d8b02b06a5e5b9a25321 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Tue, 9 Sep 2025 23:38:27 +0100 Subject: [PATCH 05/50] Add Instantly to submission frequency options --- src/languages/en.ts | 2 +- .../workflows/WorkspaceAutoReportingFrequencyPage.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index ed594818fdecc..df915e0f0550d 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1961,7 +1961,7 @@ const translations = { }, frequencyDescription: 'Choose how often you’d like expenses to submit automatically, or make it manual', frequencies: { - instant: 'Instant', + instant: 'Instantly', // s7rt sync translation on other languages weekly: 'Weekly', monthly: 'Monthly', twiceAMonth: 'Twice a month', diff --git a/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx b/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx index 1b078ead6109a..60e341e495a4a 100644 --- a/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx @@ -24,7 +24,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -type AutoReportingFrequencyKey = Exclude, 'instant'>; +type AutoReportingFrequencyKey = ValueOf; type WorkspaceAutoReportingFrequencyPageProps = WithPolicyOnyxProps & PlatformStackScreenProps; @@ -43,6 +43,7 @@ const getAutoReportingFrequencyDisplayNames = (translate: LocaleContextProps['tr [CONST.POLICY.AUTO_REPORTING_FREQUENCIES.WEEKLY]: translate('workflowsPage.frequencies.weekly'), [CONST.POLICY.AUTO_REPORTING_FREQUENCIES.SEMI_MONTHLY]: translate('workflowsPage.frequencies.twiceAMonth'), [CONST.POLICY.AUTO_REPORTING_FREQUENCIES.TRIP]: translate('workflowsPage.frequencies.byTrip'), + [CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT]: translate('workflowsPage.frequencies.instant'), [CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MANUAL]: translate('workflowsPage.frequencies.manually'), }); From 50f57c9b85da75570c056de72a270ac3d29e713f Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Tue, 9 Sep 2025 23:49:14 +0100 Subject: [PATCH 06/50] =?UTF-8?q?Rename=20=E2=80=9CDelayed=20submission?= =?UTF-8?q?=E2=80=9D=20to=20=E2=80=9CSubmission=20frequency=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/languages/de.ts | 1 - src/languages/en.ts | 7 +++---- src/languages/es.ts | 1 - src/languages/fr.ts | 1 - src/languages/it.ts | 1 - src/languages/ja.ts | 1 - src/languages/nl.ts | 1 - src/languages/pl.ts | 1 - src/languages/pt-BR.ts | 1 - src/languages/zh-hans.ts | 1 - src/libs/actions/Policy/Policy.ts | 2 +- .../workflows/WorkspaceAutoReportingFrequencyPage.tsx | 2 +- src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 8 ++++---- 13 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 8dc5400581bbd..f10fc0a3d9888 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2019,7 +2019,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: 'Verspätete Einreichung konnte nicht geändert werden. Bitte versuchen Sie es erneut oder kontaktieren Sie den Support.', autoReportingFrequencyErrorMessage: 'Die Einreichungshäufigkeit konnte nicht geändert werden. Bitte versuchen Sie es erneut oder kontaktieren Sie den Support.', monthlyOffsetErrorMessage: 'Die monatliche Frequenz konnte nicht geändert werden. Bitte versuchen Sie es erneut oder kontaktieren Sie den Support.', }, diff --git a/src/languages/en.ts b/src/languages/en.ts index df915e0f0550d..b55127f8bf701 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -651,6 +651,7 @@ const translations = { unstableInternetConnection: 'Unstable internet connection. Please check your network and try again.', enableGlobalReimbursements: 'Enable Global Reimbursements', purchaseAmount: 'Purchase amount', + frequency: 'Frequency', }, supportalNoAccess: { title: 'Not so fast', @@ -1945,9 +1946,8 @@ const translations = { workflowsPage: { workflowTitle: 'Spend', workflowDescription: 'Configure a workflow from the moment spend occurs, including approval and payment.', - delaySubmissionTitle: 'Delay submissions', - delaySubmissionDescription: 'Choose a custom schedule for submitting expenses, or leave this off for realtime updates on spending.', submissionFrequency: 'Submission frequency', + submissionFrequencyDescription: 'Choose a custom schedule for submitting expenses, or leave this off for realtime updates on spending.', submissionFrequencyDateOfMonth: 'Date of month', addApprovalsTitle: 'Add approvals', addApprovalButton: 'Add approval workflow', @@ -1961,7 +1961,7 @@ const translations = { }, frequencyDescription: 'Choose how often you’d like expenses to submit automatically, or make it manual', frequencies: { - instant: 'Instantly', // s7rt sync translation on other languages + instant: 'Instantly', // s77rt sync translation on other languages weekly: 'Weekly', monthly: 'Monthly', twiceAMonth: 'Twice a month', @@ -1999,7 +1999,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: "Delayed submission couldn't be changed. Please try again or contact support.", autoReportingFrequencyErrorMessage: "Submission frequency couldn't be changed. Please try again or contact support.", monthlyOffsetErrorMessage: "Monthly frequency couldn't be changed. Please try again or contact support.", }, diff --git a/src/languages/es.ts b/src/languages/es.ts index 3e5aa4950d721..b128bfc1fe193 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1994,7 +1994,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: 'El parámetro de envío retrasado no pudo ser cambiado. Por favor, inténtelo de nuevo o contacte al soporte.', autoReportingFrequencyErrorMessage: 'La frecuencia de envío no pudo ser cambiada. Por favor, inténtelo de nuevo o contacte al soporte.', monthlyOffsetErrorMessage: 'La frecuencia mensual no pudo ser cambiada. Por favor, inténtelo de nuevo o contacte al soporte.', }, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 36b147392dee2..2e2ab4b2e3c41 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2023,7 +2023,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: "La soumission retardée n'a pas pu être modifiée. Veuillez réessayer ou contacter le support.", autoReportingFrequencyErrorMessage: "La fréquence de soumission n'a pas pu être modifiée. Veuillez réessayer ou contacter le support.", monthlyOffsetErrorMessage: "La fréquence mensuelle n'a pas pu être modifiée. Veuillez réessayer ou contacter le support.", }, diff --git a/src/languages/it.ts b/src/languages/it.ts index 3933ba70b6e5d..c98a9bb4c47cc 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2013,7 +2013,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: 'La presentazione ritardata non può essere modificata. Per favore, riprova o contatta il supporto.', autoReportingFrequencyErrorMessage: "La frequenza di invio non può essere modificata. Riprova o contatta l'assistenza.", monthlyOffsetErrorMessage: 'La frequenza mensile non può essere modificata. Riprova o contatta il supporto.', }, diff --git a/src/languages/ja.ts b/src/languages/ja.ts index ec1b085c3d207..7b59988f4b02d 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2007,7 +2007,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: '遅延した提出は変更できませんでした。もう一度お試しいただくか、サポートにお問い合わせください。', autoReportingFrequencyErrorMessage: '提出頻度を変更できませんでした。もう一度お試しいただくか、サポートにお問い合わせください。', monthlyOffsetErrorMessage: '月次の頻度を変更できませんでした。もう一度お試しいただくか、サポートにお問い合わせください。', }, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 2e8df60bf56b7..99c2ff3b49c59 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2013,7 +2013,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: 'Vertraagde inzending kon niet worden gewijzigd. Probeer het opnieuw of neem contact op met de ondersteuning.', autoReportingFrequencyErrorMessage: 'De frequentie van inzendingen kon niet worden gewijzigd. Probeer het opnieuw of neem contact op met de ondersteuning.', monthlyOffsetErrorMessage: 'Maandelijkse frequentie kon niet worden gewijzigd. Probeer het opnieuw of neem contact op met de ondersteuning.', }, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index b6dc85151037b..56a49f0d5a6be 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2010,7 +2010,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: 'Opóźnione zgłoszenie nie mogło zostać zmienione. Spróbuj ponownie lub skontaktuj się z pomocą techniczną.', autoReportingFrequencyErrorMessage: 'Nie można było zmienić częstotliwości przesyłania. Spróbuj ponownie lub skontaktuj się z pomocą techniczną.', monthlyOffsetErrorMessage: 'Nie można było zmienić miesięcznej częstotliwości. Spróbuj ponownie lub skontaktuj się z pomocą techniczną.', }, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index e1df65f63c3cb..580fd897611e7 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2013,7 +2013,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: 'A submissão atrasada não pôde ser alterada. Por favor, tente novamente ou entre em contato com o suporte.', autoReportingFrequencyErrorMessage: 'A frequência de envio não pôde ser alterada. Por favor, tente novamente ou entre em contato com o suporte.', monthlyOffsetErrorMessage: 'A frequência mensal não pôde ser alterada. Por favor, tente novamente ou entre em contato com o suporte.', }, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 8f8aab74484c2..92bf710a45e14 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -1987,7 +1987,6 @@ const translations = { }, }, workflowsDelayedSubmissionPage: { - autoReportingErrorMessage: '延迟提交无法更改。请重试或联系客服。', autoReportingFrequencyErrorMessage: '提交频率无法更改。请重试或联系客服。', monthlyOffsetErrorMessage: '无法更改每月频率。请重试或联系支持。', }, diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index c9fd59fb1027f..79a4ed441a1b5 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -610,7 +610,7 @@ function setWorkspaceAutoHarvesting(policy: OnyxEntry, enabled: boolean) autoReporting: null, }, errorFields: { - autoReporting: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workflowsDelayedSubmissionPage.autoReportingErrorMessage'), + autoReporting: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workflowsDelayedSubmissionPage.autoReportingFrequencyErrorMessage'), }, }, }, diff --git a/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx b/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx index 60e341e495a4a..3cc5849379200 100644 --- a/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx @@ -123,7 +123,7 @@ function WorkspaceAutoReportingFrequencyPage({policy, route}: WorkspaceAutoRepor addBottomSafeAreaPadding > setWorkspaceAutoHarvesting(policy, isEnabled), subMenuItems: ( Date: Wed, 10 Sep 2025 01:56:42 +0100 Subject: [PATCH 07/50] translate --- src/languages/de.ts | 4 ++-- src/languages/en.ts | 2 +- src/languages/es.ts | 6 +++--- src/languages/fr.ts | 8 ++++---- src/languages/it.ts | 6 +++--- src/languages/ja.ts | 6 +++--- src/languages/nl.ts | 6 +++--- src/languages/pl.ts | 8 ++++---- src/languages/pt-BR.ts | 6 +++--- src/languages/zh-hans.ts | 6 +++--- 10 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index f10fc0a3d9888..9dcc5493d60ff 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -660,6 +660,7 @@ const translations = { unstableInternetConnection: 'Instabile Internetverbindung. Bitte überprüfe dein Netzwerk und versuche es erneut.', enableGlobalReimbursements: 'Globale Rückerstattungen aktivieren', purchaseAmount: 'Kaufbetrag', + frequency: 'Frequenz', }, supportalNoAccess: { title: 'Nicht so schnell', @@ -1965,9 +1966,8 @@ const translations = { workflowsPage: { workflowTitle: 'Ausgaben', workflowDescription: 'Konfigurieren Sie einen Workflow ab dem Moment, in dem Ausgaben anfallen, einschließlich Genehmigung und Zahlung.', - delaySubmissionTitle: 'Einreichungen verzögern', - delaySubmissionDescription: 'Wählen Sie einen benutzerdefinierten Zeitplan für die Einreichung von Ausgaben oder lassen Sie dies für Echtzeit-Updates zu Ausgaben aus.', submissionFrequency: 'Einreichungshäufigkeit', + submissionFrequencyDescription: 'Wählen Sie einen benutzerdefinierten Zeitplan für die Einreichung von Ausgaben oder lassen Sie dies für Echtzeit-Updates zu Ausgaben aus.', submissionFrequencyDateOfMonth: 'Datum des Monats', addApprovalsTitle: 'Genehmigungen hinzufügen', addApprovalButton: 'Genehmigungsworkflow hinzufügen', diff --git a/src/languages/en.ts b/src/languages/en.ts index b55127f8bf701..02581124f0543 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1961,7 +1961,7 @@ const translations = { }, frequencyDescription: 'Choose how often you’d like expenses to submit automatically, or make it manual', frequencies: { - instant: 'Instantly', // s77rt sync translation on other languages + instant: 'Instantly', weekly: 'Weekly', monthly: 'Monthly', twiceAMonth: 'Twice a month', diff --git a/src/languages/es.ts b/src/languages/es.ts index b128bfc1fe193..565582215472d 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -641,6 +641,7 @@ const translations = { unstableInternetConnection: 'Conexión a internet inestable. Por favor, revisa tu red e inténtalo de nuevo.', enableGlobalReimbursements: 'Habilitar Reembolsos Globales', purchaseAmount: 'Importe de compra', + frequency: 'Frecuencia', }, supportalNoAccess: { title: 'No tan rápido', @@ -1940,9 +1941,8 @@ const translations = { workflowsPage: { workflowTitle: 'Gasto', workflowDescription: 'Configure un flujo de trabajo desde el momento en que se produce el gasto, incluida la aprobación y el pago', - delaySubmissionTitle: 'Retrasar envíos', - delaySubmissionDescription: 'Elige una frecuencia para enviar los gastos, o dejalo desactivado para recibir actualizaciones en tiempo real sobre los gastos.', submissionFrequency: 'Frecuencia de envíos', + submissionFrequencyDescription: 'Elige una frecuencia para enviar los gastos, o dejalo desactivado para recibir actualizaciones en tiempo real sobre los gastos.', submissionFrequencyDateOfMonth: 'Fecha del mes', addApprovalsTitle: 'Aprobaciones', addApprovalButton: 'Añadir flujo de aprobación', @@ -1956,7 +1956,7 @@ const translations = { }, frequencyDescription: 'Elige la frecuencia de presentación automática de gastos, o preséntalos manualmente', frequencies: { - instant: 'Instante', + instant: 'Al instante', weekly: 'Semanal', monthly: 'Mensual', twiceAMonth: 'Dos veces al mes', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 2e2ab4b2e3c41..e471e02b62606 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -660,6 +660,7 @@ const translations = { unstableInternetConnection: 'Connexion Internet instable. Veuillez vérifier votre réseau et réessayer.', enableGlobalReimbursements: 'Activer les remboursements globaux', purchaseAmount: "Montant de l'achat", + frequency: 'Fréquence', }, supportalNoAccess: { title: 'Pas si vite', @@ -1968,10 +1969,9 @@ const translations = { workflowsPage: { workflowTitle: 'Dépenser', workflowDescription: "Configurez un flux de travail dès que la dépense survient, y compris l'approbation et le paiement.", - delaySubmissionTitle: 'Retarder les soumissions', - delaySubmissionDescription: - 'Choisissez un calendrier personnalisé pour soumettre les dépenses, ou laissez cette option désactivée pour des mises à jour en temps réel sur les dépenses.', submissionFrequency: 'Fréquence de soumission', + submissionFrequencyDescription: + 'Choisissez un calendrier personnalisé pour soumettre les dépenses, ou laissez cette option désactivée pour des mises à jour en temps réel sur les dépenses.', submissionFrequencyDateOfMonth: 'Date du mois', addApprovalsTitle: 'Ajouter des approbations', addApprovalButton: "Ajouter un flux de travail d'approbation", @@ -1985,7 +1985,7 @@ const translations = { }, frequencyDescription: 'Choisissez la fréquence à laquelle vous souhaitez que les dépenses soient soumises automatiquement, ou faites-le manuellement.', frequencies: { - instant: 'Instantané', + instant: 'Immédiatement', weekly: 'Hebdomadaire', monthly: 'Mensuel', twiceAMonth: 'Deux fois par mois', diff --git a/src/languages/it.ts b/src/languages/it.ts index c98a9bb4c47cc..d3100b9e2ad31 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -660,6 +660,7 @@ const translations = { unstableInternetConnection: 'Connessione Internet instabile. Controlla la tua rete e riprova.', enableGlobalReimbursements: 'Abilita i rimborsi globali', purchaseAmount: 'Importo di acquisto', + frequency: 'Frequenza', }, supportalNoAccess: { title: 'Non così in fretta', @@ -1959,9 +1960,8 @@ const translations = { workflowsPage: { workflowTitle: 'Spendere', workflowDescription: 'Configura un flusso di lavoro dal momento in cui si verifica una spesa, inclusi approvazione e pagamento.', - delaySubmissionTitle: 'Ritarda invii', - delaySubmissionDescription: "Scegli un programma personalizzato per l'invio delle spese, oppure lascia disattivato per aggiornamenti in tempo reale sulle spese.", submissionFrequency: 'Frequenza di invio', + submissionFrequencyDescription: "Scegli un programma personalizzato per l'invio delle spese, oppure lascia disattivato per aggiornamenti in tempo reale sulle spese.", submissionFrequencyDateOfMonth: 'Data del mese', addApprovalsTitle: 'Aggiungi approvazioni', addApprovalButton: 'Aggiungi flusso di lavoro di approvazione', @@ -1975,7 +1975,7 @@ const translations = { }, frequencyDescription: 'Scegli con quale frequenza desideri che le spese vengano inviate automaticamente, oppure impostale manualmente.', frequencies: { - instant: 'Istantaneo', + instant: 'Immediatamente', weekly: 'Settimanale', monthly: 'Mensile', twiceAMonth: 'Due volte al mese', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 7b59988f4b02d..0a00798b44a27 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -660,6 +660,7 @@ const translations = { unstableInternetConnection: 'インターネット接続が不安定です。ネットワークを確認してもう一度お試しください。', enableGlobalReimbursements: 'グローバル払い戻しを有効にする', purchaseAmount: '購入金額', + frequency: '頻度', }, supportalNoAccess: { title: 'ちょっと待ってください', @@ -1953,9 +1954,8 @@ const translations = { workflowsPage: { workflowTitle: '支出', workflowDescription: '支出が発生した瞬間から、承認および支払いを含むワークフローを設定します。', - delaySubmissionTitle: '提出を遅らせる', - delaySubmissionDescription: '経費提出のカスタムスケジュールを選択するか、支出のリアルタイム更新のためにこれをオフにしておいてください。', submissionFrequency: '提出頻度', + submissionFrequencyDescription: '経費提出のカスタムスケジュールを選択するか、支出のリアルタイム更新のためにこれをオフにしておいてください。', submissionFrequencyDateOfMonth: '月の日付', addApprovalsTitle: '承認を追加', addApprovalButton: '承認ワークフローを追加', @@ -1969,7 +1969,7 @@ const translations = { }, frequencyDescription: '経費を自動で提出する頻度を選択するか、手動で行うように設定してください。', frequencies: { - instant: 'インスタント', + instant: '即座に', weekly: '毎週', monthly: '毎月', twiceAMonth: '月に2回', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 99c2ff3b49c59..622f8357c9bde 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -659,6 +659,7 @@ const translations = { unstableInternetConnection: 'Onstabiele internetverbinding. Controleer je netwerk en probeer het opnieuw.', enableGlobalReimbursements: 'Wereldwijde terugbetalingen inschakelen', purchaseAmount: 'Aankoopbedrag', + frequency: 'Frequentie', }, supportalNoAccess: { title: 'Niet zo snel', @@ -1959,9 +1960,8 @@ const translations = { workflowsPage: { workflowTitle: 'Uitgaven', workflowDescription: 'Configureer een workflow vanaf het moment dat uitgaven plaatsvinden, inclusief goedkeuring en betaling.', - delaySubmissionTitle: 'Vertraging van inzendingen', - delaySubmissionDescription: 'Kies een aangepast schema voor het indienen van onkosten, of laat dit uitgeschakeld voor realtime updates over uitgaven.', submissionFrequency: 'Indieningsfrequentie', + submissionFrequencyDescription: 'Kies een aangepast schema voor het indienen van onkosten, of laat dit uitgeschakeld voor realtime updates over uitgaven.', submissionFrequencyDateOfMonth: 'Datum van de maand', addApprovalsTitle: 'Goedkeuringen toevoegen', addApprovalButton: 'Goedkeuringsworkflow toevoegen', @@ -1975,7 +1975,7 @@ const translations = { }, frequencyDescription: 'Kies hoe vaak je wilt dat uitgaven automatisch worden ingediend, of maak het handmatig.', frequencies: { - instant: 'Instant', + instant: 'Onmiddellijk', weekly: 'Wekelijks', monthly: 'Maandelijks', twiceAMonth: 'Twee keer per maand', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 56a49f0d5a6be..2780faffb9678 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -660,6 +660,7 @@ const translations = { unstableInternetConnection: 'Niestabilne połączenie internetowe. Sprawdź swoją sieć i spróbuj ponownie.', enableGlobalReimbursements: 'Włącz globalne zwroty', purchaseAmount: 'Kwota zakupu', + frequency: 'Częstotliwość', }, supportalNoAccess: { title: 'Nie tak szybko', @@ -1955,10 +1956,9 @@ const translations = { workflowsPage: { workflowTitle: 'Wydatki', workflowDescription: 'Skonfiguruj przepływ pracy od momentu wystąpienia wydatku, w tym zatwierdzenie i płatność.', - delaySubmissionTitle: 'Opóźnij zgłoszenia', - delaySubmissionDescription: - 'Wybierz niestandardowy harmonogram przesyłania wydatków lub pozostaw to wyłączone, aby otrzymywać aktualizacje w czasie rzeczywistym dotyczące wydatków.', submissionFrequency: 'Częstotliwość składania wniosków', + submissionFrequencyDescription: + 'Wybierz niestandardowy harmonogram przesyłania wydatków lub pozostaw to wyłączone, aby otrzymywać aktualizacje w czasie rzeczywistym dotyczące wydatków.', submissionFrequencyDateOfMonth: 'Data miesiąca', addApprovalsTitle: 'Dodaj zatwierdzenia', addApprovalButton: 'Dodaj przepływ pracy zatwierdzania', @@ -1972,7 +1972,7 @@ const translations = { }, frequencyDescription: 'Wybierz, jak często chcesz, aby wydatki były przesyłane automatycznie, lub ustaw je na ręczne przesyłanie.', frequencies: { - instant: 'Natychmiastowy', + instant: 'Natychmiast', weekly: 'Cotygodniowo', monthly: 'Miesięczny', twiceAMonth: 'Dwa razy w miesiącu', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 580fd897611e7..e36ba0d58319a 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -660,6 +660,7 @@ const translations = { unstableInternetConnection: 'Conexão de internet instável. Verifique sua rede e tente novamente.', enableGlobalReimbursements: 'Ativar reembolsos globais', purchaseAmount: 'Valor da compra', + frequency: 'Freqüência', }, supportalNoAccess: { title: 'Não tão rápido', @@ -1959,9 +1960,8 @@ const translations = { workflowsPage: { workflowTitle: 'Gastar', workflowDescription: 'Configurar um fluxo de trabalho desde o momento em que a despesa ocorre, incluindo aprovação e pagamento.', - delaySubmissionTitle: 'Atrasar envios', - delaySubmissionDescription: 'Escolha um cronograma personalizado para enviar despesas ou deixe isso desativado para atualizações em tempo real sobre gastos.', submissionFrequency: 'Frequência de envio', + submissionFrequencyDescription: 'Escolha um cronograma personalizado para enviar despesas ou deixe isso desativado para atualizações em tempo real sobre gastos.', submissionFrequencyDateOfMonth: 'Data do mês', addApprovalsTitle: 'Adicionar aprovações', addApprovalButton: 'Adicionar fluxo de trabalho de aprovação', @@ -1975,7 +1975,7 @@ const translations = { }, frequencyDescription: 'Escolha com que frequência você gostaria que as despesas fossem enviadas automaticamente ou faça isso manualmente.', frequencies: { - instant: 'Instantâneo', + instant: 'Imediatamente', weekly: 'Semanalmente', monthly: 'Mensalmente', twiceAMonth: 'Duas vezes por mês', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 92bf710a45e14..d1c46a783c7b4 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -659,6 +659,7 @@ const translations = { unstableInternetConnection: '互联网连接不稳定。请检查你的网络,然后重试。', enableGlobalReimbursements: '启用全球报销', purchaseAmount: '购买金额', + frequency: '频率', }, supportalNoAccess: { title: '慢一点', @@ -1933,9 +1934,8 @@ const translations = { workflowsPage: { workflowTitle: '花费', workflowDescription: '配置从支出发生到审批和支付的工作流程。', - delaySubmissionTitle: '延迟提交', - delaySubmissionDescription: '选择自定义的费用提交时间表,或者关闭此选项以实时更新支出。', submissionFrequency: '提交频率', + submissionFrequencyDescription: '选择自定义的费用提交时间表,或者关闭此选项以实时更新支出。', submissionFrequencyDateOfMonth: '月份日期', addApprovalsTitle: '添加审批', addApprovalButton: '添加审批工作流程', @@ -1949,7 +1949,7 @@ const translations = { }, frequencyDescription: '选择您希望自动提交费用的频率,或者选择手动提交', frequencies: { - instant: '即时', + instant: '即刻', weekly: '每周', monthly: '每月', twiceAMonth: '每月两次', From e5c9853e9480a2b09900a08c612ab1216f0464f2 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 10 Sep 2025 14:17:47 +0100 Subject: [PATCH 08/50] update report id and name based on autoreporting --- .../MoneyRequestConfirmationListFooter.tsx | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 52a5af5e4c591..39861d51229ee 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -18,14 +18,7 @@ import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getT import {canSendInvoice, getPerDiemCustomUnit} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; -import { - buildOptimisticExpenseReport, - getDefaultWorkspaceAvatar, - getOutstandingReportsForUser, - isMoneyRequestReport, - isReportOutstanding, - populateOptimisticReportFormula, -} from '@libs/ReportUtils'; +import {getDefaultWorkspaceAvatar, getOutstandingReportsForUser, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; import {getTagVisibility, hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, @@ -245,7 +238,7 @@ function MoneyRequestConfirmationListFooter({ receiptFilename, receiptPath, reportActionID, - reportID, + reportID: reportIDProp, selectedParticipants, shouldDisplayFieldError, shouldDisplayReceipt, @@ -279,6 +272,7 @@ function MoneyRequestConfirmationListFooter({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldShowMap = isDistanceRequest && !isManualDistanceRequest && !!(hasErrors || hasPendingWaypoints || iouType !== CONST.IOU.TYPE.SPLIT || !isReadOnly); + const personalWorkspace = useMemo(() => Object.values(allPolicies ?? {}).find((p) => p?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); const senderWorkspace = useMemo(() => { const senderWorkspaceParticipant = selectedParticipants.find((participant) => participant.isSender); return allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${senderWorkspaceParticipant?.policyID}`]; @@ -301,16 +295,26 @@ function MoneyRequestConfirmationListFooter({ const firstOutstandingReport = getOutstandingReportsForUser(policyID, reportOwnerAccountID, allReports ?? {}, undefined, false) .sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? '')) .at(0); + const shouldAutoReport = policy?.autoReporting || personalWorkspace?.autoReporting; + + let reportID: string | undefined; let reportName: string | undefined; - if (shouldUseTransactionReport) { - reportName = transactionReport.reportName; - } else { - reportName = firstOutstandingReport?.reportName; - } - if (!reportName) { - const optimisticReport = buildOptimisticExpenseReport(reportID, policy?.id, policy?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID, Number(formattedAmount), currency); - reportName = populateOptimisticReportFormula(policy?.fieldList?.text_title?.defaultValue ?? '', optimisticReport, policy); + if (shouldAutoReport) { + reportID = reportIDProp; + + if (shouldUseTransactionReport) { + reportName = transactionReport.reportName; + } else { + reportName = firstOutstandingReport?.reportName; + } + + if (!reportName) { + reportName = 'New report'; // s77rt + } + } else { + reportID = ''; + reportName = 'None'; // s77rt } // When creating an expense in an individual report, the report field becomes read-only From b128ea35af3167ccc3f78d5b693a86459ce42848 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:40:58 +0100 Subject: [PATCH 09/50] track expense: use money report only if chat is not selfDM --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 966c3df86146e..142fcdb11bf4e 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3898,7 +3898,7 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T // STEP 2: If not in the self-DM flow, we need to use the expense report. // For this, first use the chatReport.iouReportID property. Build a new optimistic expense report if needed. - const shouldUseMoneyReport = !!isPolicyExpenseChat; + const shouldUseMoneyReport = !!isPolicyExpenseChat && chatReport.chatType !== CONST.REPORT.CHAT_TYPE.SELF_DM; let iouReport: OnyxInputValue = null; let shouldCreateNewMoneyRequestReport = false; From 36dc5515a271b8cf2e418c16da025e78b1966a78 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Fri, 12 Sep 2025 04:44:50 +0100 Subject: [PATCH 10/50] handle create unreported expense --- .../MoneyRequestConfirmationListFooter.tsx | 8 +++----- .../iou/request/step/IOURequestEditReportCommon.tsx | 2 +- .../iou/request/step/IOURequestStepConfirmation.tsx | 13 +++++++++---- src/pages/iou/request/step/IOURequestStepReport.tsx | 2 +- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index ce34283783644..0a7b7ad4b87ae 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -286,8 +286,6 @@ function MoneyRequestConfirmationListFooter({ const shouldShowMap = isDistanceRequest && !isManualDistanceRequest && !!(hasErrors || hasPendingWaypoints || iouType !== CONST.IOU.TYPE.SPLIT || !isReadOnly); const isFromGlobalCreate = !!transaction?.isFromGlobalCreate; - const personalWorkspace = useMemo(() => Object.values(allPolicies ?? {}).find((p) => p?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); - const senderWorkspace = useMemo(() => { const senderWorkspaceParticipant = selectedParticipants.find((participant) => participant.isSender); return allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${senderWorkspaceParticipant?.policyID}`]; @@ -303,11 +301,11 @@ function MoneyRequestConfirmationListFooter({ * We need to check if the transaction report exists first in order to prevent the outstanding reports from being used. * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. */ + const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = transaction?.reportID ? Object.values(allReports ?? {}).find((report) => report?.reportID === transaction.reportID) : undefined; const policyID = selectedParticipants?.at(0)?.policyID; const selectedPolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; const shouldUseTransactionReport = !!transactionReport && isReportOutstanding(transactionReport, policyID, undefined, false); - const shouldAutoReport = selectedPolicy?.autoReporting || personalWorkspace?.autoReporting; const availableOutstandingReports = getOutstandingReportsForUser(policyID, selectedParticipants?.at(0)?.ownerAccountID, allReports, reportNameValuePairs, false).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? ''), ); @@ -321,9 +319,9 @@ function MoneyRequestConfirmationListFooter({ return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`]; }, [allReports, selectedReportID]); - let reportName = getReportName(selectedReport, selectedPolicy); + let reportName = isUnreported ? 'None' : getReportName(selectedReport, selectedPolicy); // s77rt if (!reportName) { - reportName = shouldAutoReport ? 'New report' : 'None'; // s77rt + reportName = 'New report'; // s77rt } // When creating an expense in an individual report, the report field becomes read-only diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 766893ed7ea26..42543ab4f65a1 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -74,7 +74,7 @@ function IOURequestEditReportCommon({ .some((transaction) => transaction?.comment?.liabilityType === CONST.TRANSACTION.LIABILITY_TYPE.RESTRICT); }, [transactionIDs, selectedReport, reportTransactions]); - const shouldShowRemoveFromReport = isEditing && isOwner && !isReportIOU && !isUnreported && !isCardTransaction; + const shouldShowRemoveFromReport = true; // s77rt const expenseReports = useMemo(() => { // Early return if no reports are available to prevent useless loop diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 5bce9133b5761..b41f79380d048 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -70,7 +70,7 @@ import { import {openDraftWorkspaceRequest} from '@userActions/Policy/Policy'; import {removeDraftTransaction, removeDraftTransactions, replaceDefaultDraftTransaction} from '@userActions/TransactionEdit'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; +import ONYXKEYS, {OnyxKey} from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {RecentlyUsedCategories} from '@src/types/onyx'; @@ -145,15 +145,20 @@ function IOURequestStepConfirmation({ const [policyRecentlyUsedCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${realPolicyID}`, {canBeMissing: true}); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); + const [testSelfDMReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${6411081495238771}`, {canBeMissing: false}); // s77rt + /* * We want to use a report from the transaction if it exists * Also if the report was submitted and delayed submission is on, then we should use an initial report + * If this is an unreported expense then use the self DM as home of unreported expenses */ + const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = getReportOrDraftReport(transaction?.reportID); const shouldUseTransactionReport = transactionReport && !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, undefined, false); - const report = shouldUseTransactionReport ? transactionReport : (reportReal ?? reportDraft); + const report = isUnreported ? testSelfDMReport : shouldUseTransactionReport ? transactionReport : (reportReal ?? reportDraft); const policy = policyReal ?? policyDraft; + const policyID = isUnreported ? policy?.id : getIOURequestPolicyID(transaction, report); const isDraftPolicy = policy === policyDraft; const policyCategories = policyCategoriesReal ?? policyCategoriesDraft; const receiverParticipant: Participant | InvoiceReceiver | undefined = transaction?.participants?.find((participant) => participant?.accountID) ?? report?.invoiceReceiver; @@ -840,7 +845,7 @@ function IOURequestStepConfirmation({ return; } - if (iouType === CONST.IOU.TYPE.TRACK || isCategorizingTrackExpense || isSharingTrackExpense) { + if (iouType === CONST.IOU.TYPE.TRACK || isCategorizingTrackExpense || isSharingTrackExpense || isUnreported) { if (Object.values(receiptFiles).filter((receipt) => !!receipt).length && transaction) { // If the transaction amount is zero, then the money is being requested through the "Scan" flow and the GPS coordinates need to be included. if (transaction.amount === 0 && !isSharingTrackExpense && !isCategorizingTrackExpense && locationPermissionGranted) { @@ -1157,7 +1162,7 @@ function IOURequestStepConfirmation({ reportID={reportID} shouldDisplayReceipt={!isMovingTransactionFromTrackExpense && !isDistanceRequest && !isPerDiemRequest} isPolicyExpenseChat={isPolicyExpenseChat} - policyID={getIOURequestPolicyID(transaction, report)} + policyID={policyID} iouMerchant={transaction?.merchant} iouCreated={transaction?.created} isDistanceRequest={isDistanceRequest} diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index b5e331da9af59..80fbfaa0611e2 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -72,7 +72,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { setTransactionReport( transaction.transactionID, { - reportID: item.value, + reportID: CONST.REPORT.UNREPORTED_REPORT_ID, // s77rt participants, }, true, From 7bbea19243e664b52ce0ff6ec47600be6b937553 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:22:56 +0100 Subject: [PATCH 11/50] start flow with unreported report if autoreporting is off --- .../FloatingActionButtonAndPopover.tsx | 46 ++++++------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx index 46117fb8b18ee..c6fb498471189 100644 --- a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx @@ -57,7 +57,7 @@ import type {QuickActionName} from '@src/types/onyx/QuickAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; -type PolicySelector = Pick; +type PolicySelector = Pick; type FloatingActionButtonAndPopoverProps = { /* Callback function when the menu is shown */ @@ -87,6 +87,7 @@ const policySelector = (policy: OnyxEntry): PolicySelector => avatarURL: policy.avatarURL, name: policy.name, areInvoicesEnabled: policy.areInvoicesEnabled, + autoReporting: policy.autoReporting, }) as PolicySelector; /** @@ -147,7 +148,9 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT canBeMissing: true, selector: (policies) => Object.values(policies ?? {}).some((policy) => isPaidGroupPolicy(policy) && isPolicyMember(policy, currentUserPersonalDetails.login)), }); - + const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); + const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const reportID = useMemo(() => (shouldAutoReport ? generateReportID() : CONST.REPORT.UNREPORTED_REPORT_ID), [shouldAutoReport]); const groupPoliciesWithChatEnabled = getGroupPaidPoliciesWithExpenseChatEnabled(); /** @@ -224,9 +227,9 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT } // Start the scan flow directly - startMoneyRequest(CONST.IOU.TYPE.CREATE, generateReportID(), CONST.IOU.REQUEST_TYPE.SCAN, false, undefined, allTransactionDrafts); + startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, CONST.IOU.REQUEST_TYPE.SCAN, false, undefined, allTransactionDrafts); }); - }, [shouldRedirectToExpensifyClassic, allTransactionDrafts]); + }, [shouldRedirectToExpensifyClassic, allTransactionDrafts, reportID]); /** * Check if LHN status changed from active to inactive. @@ -307,20 +310,11 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT setModalVisible(true); return; } - startMoneyRequest( - CONST.IOU.TYPE.CREATE, - // When starting to create an expense from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used - // for all of the routes in the creation flow. - generateReportID(), - undefined, - undefined, - undefined, - allTransactionDrafts, - ); + startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, undefined, undefined, undefined, allTransactionDrafts); }), }, ]; - }, [translate, shouldRedirectToExpensifyClassic, shouldUseNarrowLayout, allTransactionDrafts]); + }, [translate, shouldRedirectToExpensifyClassic, shouldUseNarrowLayout, allTransactionDrafts, reportID]); const quickActionMenuItems = useMemo(() => { // Define common properties in baseQuickAction @@ -370,7 +364,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT return; } - const quickActionReportID = policyChatForActivePolicy?.reportID || generateReportID(); + const quickActionReportID = policyChatForActivePolicy?.reportID || reportID; startMoneyRequest(CONST.IOU.TYPE.SUBMIT, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, true, undefined, allTransactionDrafts); }); }; @@ -408,6 +402,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT isReportArchived, isManualDistanceTrackingEnabled, allTransactionDrafts, + reportID, ]); const isTravelEnabled = useMemo(() => { @@ -443,13 +438,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT return; } // Start the flow to start tracking a distance request - startDistanceRequest( - CONST.IOU.TYPE.CREATE, - // When starting to create an expense from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used - // for all of the routes in the creation flow. - generateReportID(), - lastDistanceExpenseType, - ); + startDistanceRequest(CONST.IOU.TYPE.CREATE, reportID, lastDistanceExpenseType); }); }, }, @@ -520,16 +509,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT return; } - startMoneyRequest( - CONST.IOU.TYPE.INVOICE, - // When starting to create an invoice from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used - // for all of the routes in the creation flow. - generateReportID(), - undefined, - undefined, - undefined, - allTransactionDrafts, - ); + startMoneyRequest(CONST.IOU.TYPE.INVOICE, reportID, undefined, undefined, undefined, allTransactionDrafts); }), }, ] From 042d4a09cc117a2ed21d9671b0d34be5f3acc0d2 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sat, 13 Sep 2025 01:24:14 +0100 Subject: [PATCH 12/50] allow clearing report selection on create expense flow --- .../step/IOURequestEditReportCommon.tsx | 4 +-- .../iou/request/step/IOURequestStepReport.tsx | 33 ++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 42543ab4f65a1..2b2c1331b215e 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -74,7 +74,7 @@ function IOURequestEditReportCommon({ .some((transaction) => transaction?.comment?.liabilityType === CONST.TRANSACTION.LIABILITY_TYPE.RESTRICT); }, [transactionIDs, selectedReport, reportTransactions]); - const shouldShowRemoveFromReport = true; // s77rt + const shouldShowRemoveFromReport = isOwner && !isReportIOU && !isUnreported && !isCardTransaction; const expenseReports = useMemo(() => { // Early return if no reports are available to prevent useless loop @@ -179,7 +179,7 @@ function IOURequestEditReportCommon({ shouldShowRemoveFromReport ? ( diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 80fbfaa0611e2..590def05b4758 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -72,7 +72,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { setTransactionReport( transaction.transactionID, { - reportID: CONST.REPORT.UNREPORTED_REPORT_ID, // s77rt + reportID: item.value, participants, }, true, @@ -142,17 +142,28 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { if (!transaction) { return; } - Navigation.dismissModal(); - InteractionManager.runAfterInteractions(() => { - changeTransactionsReport( - [transaction.transactionID], - CONST.REPORT.UNREPORTED_REPORT_ID, - isASAPSubmitBetaEnabled, - session?.accountID ?? CONST.DEFAULT_NUMBER_ID, - session?.email ?? '', + if (isEditing) { + Navigation.dismissModal(); + InteractionManager.runAfterInteractions(() => { + changeTransactionsReport( + [transaction.transactionID], + CONST.REPORT.UNREPORTED_REPORT_ID, + isASAPSubmitBetaEnabled, + session?.accountID ?? CONST.DEFAULT_NUMBER_ID, + session?.email ?? '', + ); + removeTransaction(transaction.transactionID); + }); + } else { + handleGoBack(); + setTransactionReport( + transaction.transactionID, + { + reportID: CONST.REPORT.UNREPORTED_REPORT_ID, + }, + true, ); - removeTransaction(transaction.transactionID); - }); + } }; // eslint-disable-next-line rulesdir/no-negated-variables From 18b803c1a0b4f7c974358fad2c9fb89aa967ffc9 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sat, 13 Sep 2025 01:57:47 +0100 Subject: [PATCH 13/50] set correct report when changing participants --- .../request/step/IOURequestStepParticipants.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index bdb9f1b7645f5..140df5e747ab8 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -56,6 +56,7 @@ const policySelector = (policy: OnyxEntry): OnyxEntry => outputCurrency: policy.outputCurrency, isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, customUnits: policy.customUnits, + autoReporting: policy.autoReporting, }; type IOURequestStepParticipantsProps = WithWritableReportOrNotFoundProps & @@ -116,6 +117,7 @@ function IOURequestStepParticipants({ const [selfDMReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, {canBeMissing: true}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false}); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true}); + const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); const isActivePolicyRequest = iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id); @@ -204,6 +206,8 @@ function IOURequestStepParticipants({ } const firstParticipantReportID = val.at(0)?.reportID; + const isPolicyExpenseChat = !!firstParticipant?.isPolicyExpenseChat; + const policy = isPolicyExpenseChat && firstParticipant?.policyID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${firstParticipant.policyID}`] : undefined; const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && isInvoiceRoomWithID(firstParticipantReportID); numberOfParticipants.current = val.length; transactions.forEach((transaction) => { @@ -213,8 +217,6 @@ function IOURequestStepParticipants({ if (!isMovingTransactionFromTrackExpense) { // If not moving the transaction from track expense, select the default rate automatically. // Otherwise, keep the original p2p rate and let the user manually change it to the one they want from the workspace. - const isPolicyExpenseChat = !!firstParticipant?.isPolicyExpenseChat; - const policy = isPolicyExpenseChat && firstParticipant?.policyID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${firstParticipant.policyID}`] : undefined; const rateID = DistanceRequestUtils.getCustomUnitRateID({reportID: firstParticipantReportID, isPolicyExpenseChat, policy, lastSelectedDistanceRates}); transactions.forEach((transaction) => { setCustomUnitRateID(transaction.transactionID, rateID); @@ -231,9 +233,13 @@ function IOURequestStepParticipants({ // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. // We use || to be sure that if the first participant doesn't have a reportID, we generate a new one. // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - selectedReportID.current = firstParticipantReportID || generateReportID(); + if (isPolicyExpenseChat && !policy?.autoReporting && !personalPolicy?.autoReporting) { + selectedReportID.current = CONST.REPORT.UNREPORTED_REPORT_ID; + } else { + selectedReportID.current = firstParticipantReportID || generateReportID(); + } }, - [iouType, transactions, isMovingTransactionFromTrackExpense, reportID, trackExpense, allPolicies, lastSelectedDistanceRates], + [iouType, transactions, isMovingTransactionFromTrackExpense, reportID, trackExpense, allPolicies, personalPolicy, lastSelectedDistanceRates], ); const goToNextStep = useCallback(() => { From 800684282d4a32d384438ac2d14e46eddb654e98 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sat, 13 Sep 2025 20:18:14 +0100 Subject: [PATCH 14/50] Revert "set correct report when changing participants" This reverts commit 18b803c1a0b4f7c974358fad2c9fb89aa967ffc9. --- .../request/step/IOURequestStepParticipants.tsx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 140df5e747ab8..bdb9f1b7645f5 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -56,7 +56,6 @@ const policySelector = (policy: OnyxEntry): OnyxEntry => outputCurrency: policy.outputCurrency, isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, customUnits: policy.customUnits, - autoReporting: policy.autoReporting, }; type IOURequestStepParticipantsProps = WithWritableReportOrNotFoundProps & @@ -117,7 +116,6 @@ function IOURequestStepParticipants({ const [selfDMReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, {canBeMissing: true}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false}); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true}); - const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); const isActivePolicyRequest = iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id); @@ -206,8 +204,6 @@ function IOURequestStepParticipants({ } const firstParticipantReportID = val.at(0)?.reportID; - const isPolicyExpenseChat = !!firstParticipant?.isPolicyExpenseChat; - const policy = isPolicyExpenseChat && firstParticipant?.policyID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${firstParticipant.policyID}`] : undefined; const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && isInvoiceRoomWithID(firstParticipantReportID); numberOfParticipants.current = val.length; transactions.forEach((transaction) => { @@ -217,6 +213,8 @@ function IOURequestStepParticipants({ if (!isMovingTransactionFromTrackExpense) { // If not moving the transaction from track expense, select the default rate automatically. // Otherwise, keep the original p2p rate and let the user manually change it to the one they want from the workspace. + const isPolicyExpenseChat = !!firstParticipant?.isPolicyExpenseChat; + const policy = isPolicyExpenseChat && firstParticipant?.policyID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${firstParticipant.policyID}`] : undefined; const rateID = DistanceRequestUtils.getCustomUnitRateID({reportID: firstParticipantReportID, isPolicyExpenseChat, policy, lastSelectedDistanceRates}); transactions.forEach((transaction) => { setCustomUnitRateID(transaction.transactionID, rateID); @@ -233,13 +231,9 @@ function IOURequestStepParticipants({ // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. // We use || to be sure that if the first participant doesn't have a reportID, we generate a new one. // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (isPolicyExpenseChat && !policy?.autoReporting && !personalPolicy?.autoReporting) { - selectedReportID.current = CONST.REPORT.UNREPORTED_REPORT_ID; - } else { - selectedReportID.current = firstParticipantReportID || generateReportID(); - } + selectedReportID.current = firstParticipantReportID || generateReportID(); }, - [iouType, transactions, isMovingTransactionFromTrackExpense, reportID, trackExpense, allPolicies, personalPolicy, lastSelectedDistanceRates], + [iouType, transactions, isMovingTransactionFromTrackExpense, reportID, trackExpense, allPolicies, lastSelectedDistanceRates], ); const goToNextStep = useCallback(() => { From 2b4cf14218b7017cbd8fcf89dc2121e26839cb6c Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sat, 13 Sep 2025 22:14:39 +0100 Subject: [PATCH 15/50] set correct report when changing participants (2) --- .../step/IOURequestStepParticipants.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index bdb9f1b7645f5..6d8b740390074 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -56,6 +56,7 @@ const policySelector = (policy: OnyxEntry): OnyxEntry => outputCurrency: policy.outputCurrency, isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, customUnits: policy.customUnits, + autoReporting: policy.autoReporting, }; type IOURequestStepParticipantsProps = WithWritableReportOrNotFoundProps & @@ -89,6 +90,8 @@ function IOURequestStepParticipants({ // We need to set selectedReportID if user has navigated back from confirmation page and navigates to confirmation page with already selected participant const selectedReportID = useRef(participants?.length === 1 ? (participants.at(0)?.reportID ?? reportID) : reportID); + // We can assume that shouldAutoReport is true as the initial value is not used. shouldAutoReport is only used after the selectedReportID changes in addParticipant where we'd update shouldAutoReport too + const shouldAutoReport = useRef(true); const numberOfParticipants = useRef(participants?.length ?? 0); const iouRequestType = getRequestType(initialTransaction, isBetaEnabled(CONST.BETAS.MANUAL_DISTANCE)); const isSplitRequest = iouType === CONST.IOU.TYPE.SPLIT; @@ -116,6 +119,7 @@ function IOURequestStepParticipants({ const [selfDMReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, {canBeMissing: true}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false}); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true}); + const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); const isActivePolicyRequest = iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id); @@ -204,6 +208,8 @@ function IOURequestStepParticipants({ } const firstParticipantReportID = val.at(0)?.reportID; + const isPolicyExpenseChat = !!firstParticipant?.isPolicyExpenseChat; + const policy = isPolicyExpenseChat && firstParticipant?.policyID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${firstParticipant.policyID}`] : undefined; const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && isInvoiceRoomWithID(firstParticipantReportID); numberOfParticipants.current = val.length; transactions.forEach((transaction) => { @@ -213,8 +219,6 @@ function IOURequestStepParticipants({ if (!isMovingTransactionFromTrackExpense) { // If not moving the transaction from track expense, select the default rate automatically. // Otherwise, keep the original p2p rate and let the user manually change it to the one they want from the workspace. - const isPolicyExpenseChat = !!firstParticipant?.isPolicyExpenseChat; - const policy = isPolicyExpenseChat && firstParticipant?.policyID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${firstParticipant.policyID}`] : undefined; const rateID = DistanceRequestUtils.getCustomUnitRateID({reportID: firstParticipantReportID, isPolicyExpenseChat, policy, lastSelectedDistanceRates}); transactions.forEach((transaction) => { setCustomUnitRateID(transaction.transactionID, rateID); @@ -225,6 +229,7 @@ function IOURequestStepParticipants({ // So we are resetting selectedReportID ref to the reportID coming from params. if (val.length !== 1 && !isInvoice) { selectedReportID.current = reportID; + shouldAutoReport.current = true; return; } @@ -232,8 +237,9 @@ function IOURequestStepParticipants({ // We use || to be sure that if the first participant doesn't have a reportID, we generate a new one. // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing selectedReportID.current = firstParticipantReportID || generateReportID(); + shouldAutoReport.current = !isPolicyExpenseChat || !!policy?.autoReporting || !!personalPolicy?.autoReporting; }, - [iouType, transactions, isMovingTransactionFromTrackExpense, reportID, trackExpense, allPolicies, lastSelectedDistanceRates], + [iouType, transactions, isMovingTransactionFromTrackExpense, reportID, trackExpense, allPolicies, personalPolicy, lastSelectedDistanceRates], ); const goToNextStep = useCallback(() => { @@ -247,11 +253,13 @@ function IOURequestStepParticipants({ } const newReportID = selectedReportID.current; + const shouldUpdateTransactipnReportID = participants?.at(0)?.reportID !== newReportID; + const transactipnReportID = shouldAutoReport.current ? newReportID : CONST.REPORT.UNREPORTED_REPORT_ID; transactions.forEach((transaction) => { setMoneyRequestTag(transaction.transactionID, ''); setMoneyRequestCategory(transaction.transactionID, ''); - if (participants?.at(0)?.reportID !== newReportID) { - setTransactionReport(transaction.transactionID, {reportID: newReportID}, true); + if (shouldUpdateTransactipnReportID) { + setTransactionReport(transaction.transactionID, {reportID: transactipnReportID}, true); } }); if ((isCategorizing || isShareAction) && numberOfParticipants.current === 0) { From a8551fcc643d4616cb3135d5565a545d3b055c71 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sat, 13 Sep 2025 22:17:52 +0100 Subject: [PATCH 16/50] get self dm report --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index b41f79380d048..6646613164516 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -42,7 +42,7 @@ import {rand64} from '@libs/NumberUtils'; import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; import {isPaidGroupPolicy} from '@libs/PolicyUtils'; -import {generateReportID, getReportOrDraftReport, isProcessingReport, isReportOutstanding, isSelectedManagerMcTest} from '@libs/ReportUtils'; +import {findSelfDMReportID, generateReportID, getReportOrDraftReport, isProcessingReport, isReportOutstanding, isSelectedManagerMcTest} from '@libs/ReportUtils'; import {getAttendees, getDefaultTaxCode, getRateID, getRequestType, getValidWaypoints, hasReceipt, isScanRequest} from '@libs/TransactionUtils'; import type {FileObject} from '@pages/media/AttachmentModalScreen/types'; import type {GpsPoint} from '@userActions/IOU'; @@ -144,8 +144,8 @@ function IOURequestStepConfirmation({ 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 [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); - - const [testSelfDMReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${6411081495238771}`, {canBeMissing: false}); // s77rt + const selfDMReportID = useMemo(() => findSelfDMReportID(), []); + const [selfDMReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, {canBeMissing: true}); /* * We want to use a report from the transaction if it exists @@ -156,7 +156,7 @@ function IOURequestStepConfirmation({ const transactionReport = getReportOrDraftReport(transaction?.reportID); const shouldUseTransactionReport = transactionReport && !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, undefined, false); - const report = isUnreported ? testSelfDMReport : shouldUseTransactionReport ? transactionReport : (reportReal ?? reportDraft); + const report = isUnreported ? selfDMReport : shouldUseTransactionReport ? transactionReport : (reportReal ?? reportDraft); const policy = policyReal ?? policyDraft; const policyID = isUnreported ? policy?.id : getIOURequestPolicyID(transaction, report); const isDraftPolicy = policy === policyDraft; From a224b9d57642ed1e6e6df521c696bb3e621325aa Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sat, 13 Sep 2025 23:03:18 +0100 Subject: [PATCH 17/50] update transaction when coming back to amount step and updating participant --- .../iou/request/step/IOURequestStepAmount.tsx | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index d62297a9e0d12..040c57c494404 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -9,6 +9,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; +import {setTransactionReport} from '@libs/actions/Transaction'; import {createDraftTransaction, removeDraftTransaction} from '@libs/actions/TransactionEdit'; import {convertToBackendAmount, isValidCurrencyCode} from '@libs/CurrencyUtils'; import {navigateToParticipantPage} from '@libs/IOUUtils'; @@ -37,15 +38,28 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {SelectedTabRequest} from '@src/types/onyx'; +import type {Policy, SelectedTabRequest} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type Transaction from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +const policySelector = (policy: OnyxEntry): OnyxEntry => + policy && { + id: policy.id, + name: policy.name, + type: policy.type, + role: policy.role, + owner: policy.owner, + outputCurrency: policy.outputCurrency, + isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, + autoReporting: policy.autoReporting, + }; + type AmountParams = { amount: string; paymentMethod?: PaymentMethodType; @@ -87,6 +101,8 @@ function IOURequestStepAmount({ const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true}); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true}); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); + const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: (policies) => mapOnyxCollectionItems(policies, policySelector), canBeMissing: true}); + const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(transactionID ? [transactionID] : []); const [reportAttributesDerived] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {canBeMissing: true, selector: (val) => val?.reports}); const isEditing = action === CONST.IOU.ACTION.EDIT; @@ -253,6 +269,9 @@ function IOURequestStepAmount({ // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; + setTransactionReport(transactionID, {reportID: transactionReportID}, true); setMoneyRequestParticipantsFromReport(transactionID, activePolicyExpenseChat).then(() => { Navigation.navigate( ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( From 93c59a9ea6ae1cc1074db75116b0d57f9599a486 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 00:13:54 +0100 Subject: [PATCH 18/50] handle report name and navigation in case no report is available --- .../MoneyRequestConfirmationListFooter.tsx | 22 ++++++++++++++----- .../FloatingActionButtonAndPopover.tsx | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 62777df92d816..579f87bf3dbc2 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -18,7 +18,17 @@ import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getT import {canSendInvoice, getPerDiemCustomUnit} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; -import {getDefaultWorkspaceAvatar, getOutstandingReportsForUser, getReportName, isArchivedReport, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; +import { + buildOptimisticExpenseReport, + generateReportID, + getDefaultWorkspaceAvatar, + getOutstandingReportsForUser, + getReportName, + isArchivedReport, + isMoneyRequestReport, + isReportOutstanding, + populateOptimisticReportFormula, +} from '@libs/ReportUtils'; import {getTagVisibility, hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, @@ -305,13 +315,13 @@ function MoneyRequestConfirmationListFooter({ const transactionReport = transaction?.reportID ? Object.values(allReports ?? {}).find((report) => report?.reportID === transaction.reportID) : undefined; const policyID = selectedParticipants?.at(0)?.policyID; const selectedPolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const shouldUseTransactionReport = !!transactionReport && isReportOutstanding(transactionReport, policyID, undefined, false); + const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, policyID, undefined, false)) || isUnreported; const availableOutstandingReports = getOutstandingReportsForUser(policyID, selectedParticipants?.at(0)?.ownerAccountID, allReports, reportNameValuePairs, false).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? ''), ); const iouReportID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.iouReportID; const outstandingReportID = isPolicyExpenseChat ? (iouReportID ?? availableOutstandingReports.at(0)?.reportID) : reportID; - let selectedReportID = shouldUseTransactionReport ? transactionReport.reportID : outstandingReportID; + let selectedReportID = shouldUseTransactionReport ? transaction?.reportID : outstandingReportID; const selectedReport = useMemo(() => { if (!selectedReportID) { return; @@ -319,9 +329,11 @@ function MoneyRequestConfirmationListFooter({ return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`]; }, [allReports, selectedReportID]); - let reportName = isUnreported ? 'None' : getReportName(selectedReport, selectedPolicy); // s77rt + let reportName = getReportName(selectedReport, selectedPolicy); if (!reportName) { - reportName = 'New report'; // s77rt + // If we could not get a report name, the selectedReportID is either optimistic or not set, if it's the latter, genereate a random id for proper navigation + selectedReportID ||= generateReportID(); + reportName = isUnreported ? 'None' : 'New report'; // s77rt } // When creating an expense in an individual report, the report field becomes read-only diff --git a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx index c6fb498471189..4231de88893cc 100644 --- a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx @@ -149,7 +149,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT selector: (policies) => Object.values(policies ?? {}).some((policy) => isPaidGroupPolicy(policy) && isPolicyMember(policy, currentUserPersonalDetails.login)), }); const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); - const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; // s77rt fix navigation param 0 const reportID = useMemo(() => (shouldAutoReport ? generateReportID() : CONST.REPORT.UNREPORTED_REPORT_ID), [shouldAutoReport]); const groupPoliciesWithChatEnabled = getGroupPaidPoliciesWithExpenseChatEnabled(); From 023d88def83572a1c32040a011d923874be5c05a Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 02:38:14 +0100 Subject: [PATCH 19/50] add s77rt comment --- src/pages/iou/request/step/IOURequestEditReportCommon.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 2b2c1331b215e..a71c2046b964e 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -156,6 +156,8 @@ function IOURequestEditReportCommon({ return isOpen && !isAdmin && !isSubmitter; }, [selectedReport, reportPolicy, expenseReports.length, shouldShowNotFoundPageFromProps]); + // s77rt add option to set no report in case we were coming from a "New report" case + return ( Date: Sun, 14 Sep 2025 17:16:30 +0100 Subject: [PATCH 20/50] allow choosing no report if default option is 'New report' --- src/pages/iou/request/step/IOURequestEditReportCommon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index a71c2046b964e..87fbf873f7938 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -74,7 +74,7 @@ function IOURequestEditReportCommon({ .some((transaction) => transaction?.comment?.liabilityType === CONST.TRANSACTION.LIABILITY_TYPE.RESTRICT); }, [transactionIDs, selectedReport, reportTransactions]); - const shouldShowRemoveFromReport = isOwner && !isReportIOU && !isUnreported && !isCardTransaction; + const shouldShowRemoveFromReport = (isOwner || !selectedReport) && !isReportIOU && !isUnreported && !isCardTransaction; const expenseReports = useMemo(() => { // Early return if no reports are available to prevent useless loop From 2ca3289848b2bb0db346d5352cddfc6482855eef Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 17:44:31 +0100 Subject: [PATCH 21/50] remove s77rt comment --- src/pages/iou/request/step/IOURequestEditReportCommon.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 87fbf873f7938..1ecae1e6206b9 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -156,8 +156,6 @@ function IOURequestEditReportCommon({ return isOpen && !isAdmin && !isSubmitter; }, [selectedReport, reportPolicy, expenseReports.length, shouldShowNotFoundPageFromProps]); - // s77rt add option to set no report in case we were coming from a "New report" case - return ( Date: Sun, 14 Sep 2025 17:55:41 +0100 Subject: [PATCH 22/50] Fix "0" in param --- src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx index 4231de88893cc..ca847986fb221 100644 --- a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx @@ -87,7 +87,6 @@ const policySelector = (policy: OnyxEntry): PolicySelector => avatarURL: policy.avatarURL, name: policy.name, areInvoicesEnabled: policy.areInvoicesEnabled, - autoReporting: policy.autoReporting, }) as PolicySelector; /** @@ -148,9 +147,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT canBeMissing: true, selector: (policies) => Object.values(policies ?? {}).some((policy) => isPaidGroupPolicy(policy) && isPolicyMember(policy, currentUserPersonalDetails.login)), }); - const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); - const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; // s77rt fix navigation param 0 - const reportID = useMemo(() => (shouldAutoReport ? generateReportID() : CONST.REPORT.UNREPORTED_REPORT_ID), [shouldAutoReport]); + const reportID = useMemo(() => generateReportID(), []); const groupPoliciesWithChatEnabled = getGroupPaidPoliciesWithExpenseChatEnabled(); /** From 1ccc35335c1abb012f79d320997ed350094be329 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 18:23:16 +0100 Subject: [PATCH 23/50] remove unnecessary picked field --- src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx index ca847986fb221..660f74d71a830 100644 --- a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx @@ -57,7 +57,7 @@ import type {QuickActionName} from '@src/types/onyx/QuickAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; -type PolicySelector = Pick; +type PolicySelector = Pick; type FloatingActionButtonAndPopoverProps = { /* Callback function when the menu is shown */ From dd9c5f61dabd2f130daa757f53357544d9c1fcb1 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 18:44:55 +0100 Subject: [PATCH 24/50] set transaction report id after setting a participant in new flow --- src/hooks/usePersonalPolicy.ts | 24 +++++++++++++++++++ src/pages/Search/SearchPage.tsx | 10 +++++++- .../step/IOURequestStepDestination.tsx | 6 +++++ .../request/step/IOURequestStepDistance.tsx | 8 ++++++- .../step/IOURequestStepDistanceManual.tsx | 7 ++++++ .../step/IOURequestStepDistanceMap.tsx | 8 ++++++- .../step/IOURequestStepScan/index.native.tsx | 11 ++++++++- .../request/step/IOURequestStepScan/index.tsx | 10 +++++++- 8 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 src/hooks/usePersonalPolicy.ts diff --git a/src/hooks/usePersonalPolicy.ts b/src/hooks/usePersonalPolicy.ts new file mode 100644 index 0000000000000..ee71cdaa1caef --- /dev/null +++ b/src/hooks/usePersonalPolicy.ts @@ -0,0 +1,24 @@ +import {useMemo} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type Policy from '@src/types/onyx/Policy'; +import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; +import useOnyx from './useOnyx'; + +type PolicySelector = Pick; + +const policySelector = (policy: OnyxEntry): PolicySelector => + (policy && { + id: policy.id, + type: policy.type, + autoReporting: policy.autoReporting, + }) as PolicySelector; + +function usePersonalPolicy() { + const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: (c) => mapOnyxCollectionItems(c, policySelector), canBeMissing: true}); + const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); + return personalPolicy; +} + +export default usePersonalPolicy; diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index e5a1dad492262..81582814d5dc4 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -25,6 +25,7 @@ import useLocalize from '@hooks/useLocalize'; import useMobileSelectionMode from '@hooks/useMobileSelectionMode'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; +import usePersonalPolicy from '@hooks/usePersonalPolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -41,6 +42,7 @@ import { search, unholdMoneyRequestOnSearch, } from '@libs/actions/Search'; +import {setTransactionReport} from '@libs/actions/Transaction'; import {navigateToParticipantPage} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -81,6 +83,7 @@ function SearchPage({route}: SearchPageProps) { const [newParentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${newReport?.parentReportID}`, {canBeMissing: true}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false}); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true}); + const personalPolicy = usePersonalPolicy(); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); const [integrationsExportTemplates] = useOnyx(ONYXKEYS.NVP_INTEGRATION_SERVER_EXPORT_TEMPLATES, {canBeMissing: true}); const [csvExportLayouts] = useOnyx(ONYXKEYS.NVP_CSV_EXPORT_LAYOUTS, {canBeMissing: true}); @@ -578,7 +581,12 @@ function SearchPage({route}: SearchPageProps) { if (isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); - const setParticipantsPromises = newReceiptFiles.map((receiptFile) => setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat)); + const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; + const setParticipantsPromises = newReceiptFiles.map((receiptFile) => { + setTransactionReport(receiptFile.transactionID, {reportID: transactionReportID}, true); + setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat); + }); Promise.all(setParticipantsPromises).then(() => Navigation.navigate( ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( diff --git a/src/pages/iou/request/step/IOURequestStepDestination.tsx b/src/pages/iou/request/step/IOURequestStepDestination.tsx index a6c6c54c03182..dbd1c62bfa682 100644 --- a/src/pages/iou/request/step/IOURequestStepDestination.tsx +++ b/src/pages/iou/request/step/IOURequestStepDestination.tsx @@ -12,9 +12,11 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; +import usePersonalPolicy from '@hooks/usePersonalPolicy'; import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import {setTransactionReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; import {getPerDiemCustomUnit, isPolicyAdmin} from '@libs/PolicyUtils'; import {getPolicyExpenseChat} from '@libs/ReportUtils'; @@ -56,6 +58,7 @@ function IOURequestStepDestination({ explicitPolicyID, }: IOURequestStepDestinationProps) { const [policy, policyMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${explicitPolicyID ?? getIOURequestPolicyID(transaction, report)}`, {canBeMissing: false}); + const personalPolicy = usePersonalPolicy(); const {accountID} = useCurrentUserPersonalDetails(); const policyExpenseReport = policy?.id ? getPolicyExpenseChat(accountID, policy.id) : undefined; const {top} = useSafeAreaInsets(); @@ -84,6 +87,9 @@ function IOURequestStepDestination({ } if (selectedDestination !== destination.keyForList) { if (openedFromStartPage) { + const shouldAutoReport = policy?.autoReporting || personalPolicy?.autoReporting; + const transactionReportID = shouldAutoReport ? policyExpenseReport?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; + setTransactionReport(transactionID, {reportID: transactionReportID}, true); setMoneyRequestParticipantsFromReport(transactionID, policyExpenseReport); setCustomUnitID(transactionID, customUnit.customUnitID); setMoneyRequestCategory(transactionID, customUnit?.defaultCategory ?? ''); diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 0fde2274f711f..e5c477a574ed9 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -18,6 +18,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePersonalPolicy from '@hooks/usePersonalPolicy'; import usePolicy from '@hooks/usePolicy'; import usePrevious from '@hooks/usePrevious'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; @@ -38,7 +39,7 @@ import { } from '@libs/actions/IOU'; import {init, stop} from '@libs/actions/MapboxToken'; import {openReport} from '@libs/actions/Report'; -import {openDraftDistanceExpense, removeWaypoint, updateWaypoints as updateWaypointsUtil} from '@libs/actions/Transaction'; +import {openDraftDistanceExpense, removeWaypoint, setTransactionReport, updateWaypoints as updateWaypointsUtil} from '@libs/actions/Transaction'; import {createBackupTransaction, removeBackupTransaction, restoreOriginalTransactionFromBackup} from '@libs/actions/TransactionEdit'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import type {MileageRate} from '@libs/DistanceRequestUtils'; @@ -85,6 +86,7 @@ function IOURequestStepDistance({ const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true}); const [transactionBackup] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_BACKUP}${transactionID}`, {canBeMissing: true}); const policy = usePolicy(report?.policyID); + const personalPolicy = usePersonalPolicy(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false}); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: false}); @@ -388,6 +390,9 @@ function IOURequestStepDistance({ policy: activePolicy, lastSelectedDistanceRates, }); + const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; + setTransactionReport(transactionID, {reportID: transactionReportID}, true); setCustomUnitRateID(transactionID, rateID); setMoneyRequestParticipantsFromReport(transactionID, activePolicyExpenseChat).then(() => { Navigation.navigate( @@ -409,6 +414,7 @@ function IOURequestStepDistance({ reportNameValuePairs, iouType, activePolicy, + personalPolicy, setDistanceRequestData, shouldSkipConfirmation, transactionID, diff --git a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx index 0d09f55ab5502..2cc09f603deba 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx @@ -10,6 +10,7 @@ import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalD import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import usePersonalPolicy from '@hooks/usePersonalPolicy'; import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -23,6 +24,7 @@ import { setMoneyRequestPendingFields, trackExpense, } from '@libs/actions/IOU'; +import {setTransactionReport} from '@libs/actions/Transaction'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import {navigateToParticipantPage} from '@libs/IOUUtils'; @@ -72,6 +74,7 @@ function IOURequestStepDistanceManual({ const [selectedTab, selectedTabResult] = useOnyx(`${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.DISTANCE_REQUEST_TYPE}`, {canBeMissing: true}); const isLoadingSelectedTab = isLoadingOnyxValue(selectedTabResult); const policy = usePolicy(report?.policyID); + const personalPolicy = usePersonalPolicy(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false}); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: false}); @@ -227,6 +230,9 @@ function IOURequestStepDistanceManual({ policy: activePolicy, lastSelectedDistanceRates, }); + const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; + setTransactionReport(transactionID, {reportID: transactionReportID}, true); setCustomUnitRateID(transactionID, rateID); setMoneyRequestParticipantsFromReport(transactionID, activePolicyExpenseChat).then(() => { Navigation.navigate( @@ -255,6 +261,7 @@ function IOURequestStepDistanceManual({ reportNameValuePairs, isCreatingNewRequest, activePolicy, + personalPolicy, shouldSkipConfirmation, personalDetails, reportAttributesDerived, diff --git a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx index 12448732841eb..20f9d12e3e273 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx @@ -18,6 +18,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePersonalPolicy from '@hooks/usePersonalPolicy'; import usePolicy from '@hooks/usePolicy'; import usePrevious from '@hooks/usePrevious'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; @@ -38,7 +39,7 @@ import { } from '@libs/actions/IOU'; import {init, stop} from '@libs/actions/MapboxToken'; import {openReport} from '@libs/actions/Report'; -import {openDraftDistanceExpense, removeWaypoint, updateWaypoints as updateWaypointsUtil} from '@libs/actions/Transaction'; +import {openDraftDistanceExpense, removeWaypoint, setTransactionReport, updateWaypoints as updateWaypointsUtil} from '@libs/actions/Transaction'; import {createBackupTransaction, removeBackupTransaction, restoreOriginalTransactionFromBackup} from '@libs/actions/TransactionEdit'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import type {MileageRate} from '@libs/DistanceRequestUtils'; @@ -85,6 +86,7 @@ function IOURequestStepDistanceMap({ const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true}); const [transactionBackup] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_BACKUP}${transactionID}`, {canBeMissing: true}); const policy = usePolicy(report?.policyID); + const personalPolicy = usePersonalPolicy(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false}); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: false}); @@ -388,6 +390,9 @@ function IOURequestStepDistanceMap({ policy: activePolicy, lastSelectedDistanceRates, }); + const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; + setTransactionReport(transactionID, {reportID: transactionReportID}, true); setCustomUnitRateID(transactionID, rateID); setMoneyRequestParticipantsFromReport(transactionID, activePolicyExpenseChat).then(() => { Navigation.navigate( @@ -409,6 +414,7 @@ function IOURequestStepDistanceMap({ reportNameValuePairs, iouType, activePolicy, + personalPolicy, setDistanceRequestData, shouldSkipConfirmation, transactionID, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index b37d64cf9f70a..e51676f1612e3 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -29,10 +29,12 @@ import useIOUUtils from '@hooks/useIOUUtils'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePersonalPolicy from '@hooks/usePersonalPolicy'; import usePolicy from '@hooks/usePolicy'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import setTestReceipt from '@libs/actions/setTestReceipt'; +import {setTransactionReport} from '@libs/actions/Transaction'; import {dismissProductTraining} from '@libs/actions/Welcome'; import {readFileAsync, showCameraPermissionsAlert} from '@libs/fileDownload/FileUtils'; import getPhotoSource from '@libs/fileDownload/getPhotoSource'; @@ -110,6 +112,7 @@ function IOURequestStepScan({ const [receiptFiles, setReceiptFiles] = useState([]); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true}); const policy = usePolicy(report?.policyID); + const personalPolicy = usePersonalPolicy(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const [skipConfirmation] = useOnyx(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${initialTransactionID}`, {canBeMissing: true}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false}); @@ -461,6 +464,8 @@ function IOURequestStepScan({ // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; // If the initial transaction has different participants selected that means that the user has changed the participant in the confirmation step if (initialTransaction?.participants && initialTransaction?.participants?.at(0)?.reportID !== activePolicyExpenseChat?.reportID) { @@ -477,7 +482,10 @@ function IOURequestStepScan({ return; } - const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat)); + const setParticipantsPromises = files.map((receiptFile) => { + setTransactionReport(receiptFile.transactionID, {reportID: transactionReportID}, true); + setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat); + }); Promise.all(setParticipantsPromises).then(() => Navigation.navigate( ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( @@ -498,6 +506,7 @@ function IOURequestStepScan({ reportNameValuePairs, iouType, activePolicy, + personalPolicy, initialTransactionID, navigateToConfirmationPage, shouldSkipConfirmation, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index b9e05dbb8b508..8b0bddcd78354 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -31,11 +31,13 @@ import useIOUUtils from '@hooks/useIOUUtils'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePersonalPolicy from '@hooks/usePersonalPolicy'; import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import setTestReceipt from '@libs/actions/setTestReceipt'; +import {setTransactionReport} from '@libs/actions/Transaction'; import {clearUserLocation, setUserLocation} from '@libs/actions/UserLocation'; import {dismissProductTraining} from '@libs/actions/Welcome'; import {isMobile, isMobileWebKit} from '@libs/Browser'; @@ -116,6 +118,7 @@ function IOURequestStepScan({ const getScreenshotTimeoutRef = useRef(null); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true}); const policy = usePolicy(report?.policyID); + const personalPolicy = usePersonalPolicy(); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const [skipConfirmation] = useOnyx(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${initialTransactionID}`, {canBeMissing: true}); @@ -517,6 +520,8 @@ function IOURequestStepScan({ // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; // If the initial transaction has different participants selected that means that the user has changed the participant in the confirmation step if (initialTransaction?.participants && initialTransaction?.participants?.at(0)?.reportID !== activePolicyExpenseChat?.reportID) { @@ -533,7 +538,10 @@ function IOURequestStepScan({ return; } - const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat)); + const setParticipantsPromises = files.map((receiptFile) => { + setTransactionReport(receiptFile.transactionID, {reportID: transactionReportID}, true); + setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat); + }); Promise.all(setParticipantsPromises).then(() => Navigation.navigate( ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( From 292751a764dbb0faa4770d8a3e71c533ada45d9a Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 20:28:10 +0100 Subject: [PATCH 25/50] clean up --- .../MoneyRequestConfirmationListFooter.tsx | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 579f87bf3dbc2..c521bcdc64e1b 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -321,20 +321,23 @@ function MoneyRequestConfirmationListFooter({ ); const iouReportID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.iouReportID; const outstandingReportID = isPolicyExpenseChat ? (iouReportID ?? availableOutstandingReports.at(0)?.reportID) : reportID; - let selectedReportID = shouldUseTransactionReport ? transaction?.reportID : outstandingReportID; - const selectedReport = useMemo(() => { - if (!selectedReportID) { - return; + const [selectedReportID, selectedReport] = useMemo(() => { + const reportIDToUse = shouldUseTransactionReport ? transaction?.reportID : outstandingReportID; + if (!reportIDToUse) { + // Even if we have no report to use we still need a report id for proper navigation + return [generateReportID(), undefined]; } - return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`]; - }, [allReports, selectedReportID]); - - let reportName = getReportName(selectedReport, selectedPolicy); - if (!reportName) { - // If we could not get a report name, the selectedReportID is either optimistic or not set, if it's the latter, genereate a random id for proper navigation - selectedReportID ||= generateReportID(); - reportName = isUnreported ? 'None' : 'New report'; // s77rt - } + + const reportToUse = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportIDToUse}`]; + return [reportIDToUse, reportToUse]; + }, [allReports, shouldUseTransactionReport, transaction?.reportID, outstandingReportID]); + const reportName = useMemo(() => { + const name = getReportName(selectedReport, selectedPolicy); + if (!name) { + return isUnreported ? 'None' : 'New report'; // s77rt + } + return name; + }, [isUnreported, selectedReport, selectedPolicy]); // When creating an expense in an individual report, the report field becomes read-only // since the destination is already determined and there's no need to show a selectable list. From edf7d3d2ec3c7bc0f20f544fe2f19b570c2c55db Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 20:37:03 +0100 Subject: [PATCH 26/50] translate --- src/components/MoneyRequestConfirmationListFooter.tsx | 4 ++-- src/languages/de.ts | 1 + src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/languages/fr.ts | 1 + src/languages/it.ts | 1 + src/languages/ja.ts | 1 + src/languages/nl.ts | 1 + src/languages/pl.ts | 1 + src/languages/pt-BR.ts | 1 + src/languages/zh-hans.ts | 1 + 11 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index c521bcdc64e1b..04a62bc5beee5 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -334,10 +334,10 @@ function MoneyRequestConfirmationListFooter({ const reportName = useMemo(() => { const name = getReportName(selectedReport, selectedPolicy); if (!name) { - return isUnreported ? 'None' : 'New report'; // s77rt + return isUnreported ? translate('common.none') : translate('common.newReport'); } return name; - }, [isUnreported, selectedReport, selectedPolicy]); + }, [isUnreported, selectedReport, selectedPolicy, translate]); // When creating an expense in an individual report, the report field becomes read-only // since the destination is already determined and there's no need to show a selectable list. diff --git a/src/languages/de.ts b/src/languages/de.ts index 037b34972f2b5..a6b60d5b47f11 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -667,6 +667,7 @@ const translations = { enableGlobalReimbursements: 'Globale Rückerstattungen aktivieren', purchaseAmount: 'Kaufbetrag', frequency: 'Frequenz', + newReport: 'Neuer Bericht', }, supportalNoAccess: { title: 'Nicht so schnell', diff --git a/src/languages/en.ts b/src/languages/en.ts index 37cacfa892634..1b0e24163cdcd 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -658,6 +658,7 @@ const translations = { enableGlobalReimbursements: 'Enable Global Reimbursements', purchaseAmount: 'Purchase amount', frequency: 'Frequency', + newReport: 'New report', }, supportalNoAccess: { title: 'Not so fast', diff --git a/src/languages/es.ts b/src/languages/es.ts index cf3269a68f1ee..526ad802e07dd 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -648,6 +648,7 @@ const translations = { enableGlobalReimbursements: 'Habilitar Reembolsos Globales', purchaseAmount: 'Importe de compra', frequency: 'Frecuencia', + newReport: 'Nuevo informe', }, supportalNoAccess: { title: 'No tan rápido', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 7a9ee7a27c193..73eaa8a314aad 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -667,6 +667,7 @@ const translations = { enableGlobalReimbursements: 'Activer les remboursements globaux', purchaseAmount: "Montant de l'achat", frequency: 'Fréquence', + newReport: 'Nouveau rapport', }, supportalNoAccess: { title: 'Pas si vite', diff --git a/src/languages/it.ts b/src/languages/it.ts index 75f7db196c2ef..30b139d8eb731 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -667,6 +667,7 @@ const translations = { enableGlobalReimbursements: 'Abilita i rimborsi globali', purchaseAmount: 'Importo di acquisto', frequency: 'Frequenza', + newReport: 'Nuovo rapporto', }, supportalNoAccess: { title: 'Non così in fretta', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 50022e0911fad..17e6a9b62a373 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -667,6 +667,7 @@ const translations = { enableGlobalReimbursements: 'グローバル払い戻しを有効にする', purchaseAmount: '購入金額', frequency: '頻度', + newReport: '新しいレポート', }, supportalNoAccess: { title: 'ちょっと待ってください', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 405e9bb724ba9..63eba9a7b541c 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -666,6 +666,7 @@ const translations = { enableGlobalReimbursements: 'Wereldwijde terugbetalingen inschakelen', purchaseAmount: 'Aankoopbedrag', frequency: 'Frequentie', + newReport: 'Nieuw rapport', }, supportalNoAccess: { title: 'Niet zo snel', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index f48a561eb3a61..1966ed012d945 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -667,6 +667,7 @@ const translations = { enableGlobalReimbursements: 'Włącz globalne zwroty', purchaseAmount: 'Kwota zakupu', frequency: 'Częstotliwość', + newReport: 'Nowy raport', }, supportalNoAccess: { title: 'Nie tak szybko', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index e56ee25624187..e4c485a52e7bf 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -667,6 +667,7 @@ const translations = { enableGlobalReimbursements: 'Ativar reembolsos globais', purchaseAmount: 'Valor da compra', frequency: 'Freqüência', + newReport: 'Novo relatório', }, supportalNoAccess: { title: 'Não tão rápido', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index ad9467f38a0a0..261c99e2842d4 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -666,6 +666,7 @@ const translations = { enableGlobalReimbursements: '启用全球报销', purchaseAmount: '购买金额', frequency: '频率', + newReport: '新报告', }, supportalNoAccess: { title: '慢一点', From 17fe33e5cce8929d605e06fc0d393d5498b5f0bc Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 20:46:52 +0100 Subject: [PATCH 27/50] ts --- src/libs/actions/Policy/Policy.ts | 4 ++-- src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 0448bbf981e1f..075c3eb54a07a 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -579,8 +579,8 @@ function deleteWorkspace(policyID: string, policyName: string, lastAccessedWorks /* Set the auto harvesting on a workspace. This goes in tandem with auto reporting. so when you enable/disable * harvesting, you are enabling/disabling auto reporting too. */ -function setWorkspaceAutoHarvesting(policy: OnyxEntry, enabled: boolean) { - const policyID = policy?.id; +function setWorkspaceAutoHarvesting(policy: Policy, enabled: boolean) { + const policyID = policy.id; const optimisticData: OnyxUpdate[] = [ { diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index fdafce1bb6c08..d2a1ce6ad88c7 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -172,7 +172,7 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { title: translate('workflowsPage.submissionFrequency'), subtitle: translate('workflowsPage.submissionFrequencyDescription'), switchAccessibilityLabel: translate('workflowsPage.submissionFrequencyDescription'), - onToggle: (isEnabled: boolean) => setWorkspaceAutoHarvesting(policy, isEnabled), + onToggle: (isEnabled: boolean) => (policy ? setWorkspaceAutoHarvesting(policy, isEnabled) : undefined), subMenuItems: ( Date: Sun, 14 Sep 2025 21:43:58 +0100 Subject: [PATCH 28/50] lint --- src/pages/Search/SearchPage.tsx | 4 +++- src/pages/iou/request/step/IOURequestStepAmount.tsx | 4 +++- .../iou/request/step/IOURequestStepConfirmation.tsx | 12 ++++++++++-- .../iou/request/step/IOURequestStepDestination.tsx | 2 ++ .../iou/request/step/IOURequestStepDistance.tsx | 4 +++- .../request/step/IOURequestStepDistanceManual.tsx | 4 +++- .../iou/request/step/IOURequestStepDistanceMap.tsx | 4 +++- .../request/step/IOURequestStepScan/index.native.tsx | 4 +++- .../iou/request/step/IOURequestStepScan/index.tsx | 5 ++++- .../workspace/workflows/WorkspaceWorkflowsPage.tsx | 7 +------ 10 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 81582814d5dc4..a737ba5fa7858 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -581,11 +581,13 @@ function SearchPage({route}: SearchPageProps) { if (isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; const setParticipantsPromises = newReceiptFiles.map((receiptFile) => { setTransactionReport(receiptFile.transactionID, {reportID: transactionReportID}, true); - setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat); + return setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat); }); Promise.all(setParticipantsPromises).then(() => Navigation.navigate( diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 040c57c494404..d4d872403ac0a 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -102,7 +102,7 @@ function IOURequestStepAmount({ const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true}); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: (policies) => mapOnyxCollectionItems(policies, policySelector), canBeMissing: true}); - const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); + const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((p) => p?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(transactionID ? [transactionID] : []); const [reportAttributesDerived] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {canBeMissing: true, selector: (val) => val?.reports}); const isEditing = action === CONST.IOU.ACTION.EDIT; @@ -269,6 +269,8 @@ function IOURequestStepAmount({ // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 6646613164516..98e41488a69f5 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -70,7 +70,7 @@ import { import {openDraftWorkspaceRequest} from '@userActions/Policy/Policy'; import {removeDraftTransaction, removeDraftTransactions, replaceDefaultDraftTransaction} from '@userActions/TransactionEdit'; import CONST from '@src/CONST'; -import ONYXKEYS, {OnyxKey} from '@src/ONYXKEYS'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {RecentlyUsedCategories} from '@src/types/onyx'; @@ -156,7 +156,15 @@ function IOURequestStepConfirmation({ const transactionReport = getReportOrDraftReport(transaction?.reportID); const shouldUseTransactionReport = transactionReport && !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, undefined, false); - const report = isUnreported ? selfDMReport : shouldUseTransactionReport ? transactionReport : (reportReal ?? reportDraft); + const report = useMemo(() => { + if (isUnreported) { + return selfDMReport; + } + if (shouldUseTransactionReport) { + return transactionReport; + } + return reportReal ?? reportDraft; + }, [isUnreported, selfDMReport, shouldUseTransactionReport, transactionReport, reportReal, reportDraft]); const policy = policyReal ?? policyDraft; const policyID = isUnreported ? policy?.id : getIOURequestPolicyID(transaction, report); const isDraftPolicy = policy === policyDraft; diff --git a/src/pages/iou/request/step/IOURequestStepDestination.tsx b/src/pages/iou/request/step/IOURequestStepDestination.tsx index dbd1c62bfa682..dfea98d5881c6 100644 --- a/src/pages/iou/request/step/IOURequestStepDestination.tsx +++ b/src/pages/iou/request/step/IOURequestStepDestination.tsx @@ -87,6 +87,8 @@ function IOURequestStepDestination({ } if (selectedDestination !== destination.keyForList) { if (openedFromStartPage) { + // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldAutoReport = policy?.autoReporting || personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? policyExpenseReport?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index e5c477a574ed9..41fe7936ec628 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -390,6 +390,8 @@ function IOURequestStepDistance({ policy: activePolicy, lastSelectedDistanceRates, }); + // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); @@ -414,7 +416,7 @@ function IOURequestStepDistance({ reportNameValuePairs, iouType, activePolicy, - personalPolicy, + personalPolicy?.autoReporting, setDistanceRequestData, shouldSkipConfirmation, transactionID, diff --git a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx index 2cc09f603deba..3b73311dbe3f5 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx @@ -230,6 +230,8 @@ function IOURequestStepDistanceManual({ policy: activePolicy, lastSelectedDistanceRates, }); + // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); @@ -261,7 +263,7 @@ function IOURequestStepDistanceManual({ reportNameValuePairs, isCreatingNewRequest, activePolicy, - personalPolicy, + personalPolicy?.autoReporting, shouldSkipConfirmation, personalDetails, reportAttributesDerived, diff --git a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx index 20f9d12e3e273..a83e68e36e6f7 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx @@ -390,6 +390,8 @@ function IOURequestStepDistanceMap({ policy: activePolicy, lastSelectedDistanceRates, }); + // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); @@ -414,7 +416,7 @@ function IOURequestStepDistanceMap({ reportNameValuePairs, iouType, activePolicy, - personalPolicy, + personalPolicy?.autoReporting, setDistanceRequestData, shouldSkipConfirmation, transactionID, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index e51676f1612e3..5b03192dbfde0 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -464,6 +464,8 @@ function IOURequestStepScan({ // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; @@ -484,7 +486,7 @@ function IOURequestStepScan({ const setParticipantsPromises = files.map((receiptFile) => { setTransactionReport(receiptFile.transactionID, {reportID: transactionReportID}, true); - setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat); + return setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat); }); Promise.all(setParticipantsPromises).then(() => Navigation.navigate( diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 8b0bddcd78354..39769ae6dd7ec 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -520,6 +520,8 @@ function IOURequestStepScan({ // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; @@ -540,7 +542,7 @@ function IOURequestStepScan({ const setParticipantsPromises = files.map((receiptFile) => { setTransactionReport(receiptFile.transactionID, {reportID: transactionReportID}, true); - setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat); + return setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat); }); Promise.all(setParticipantsPromises).then(() => Navigation.navigate( @@ -562,6 +564,7 @@ function IOURequestStepScan({ reportNameValuePairs, iouType, activePolicy, + personalPolicy?.autoReporting, initialTransactionID, navigateToConfirmationPage, shouldSkipConfirmation, diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index d2a1ce6ad88c7..ffe7e22a5cd6e 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -30,7 +30,6 @@ import { setIsForcedToChangeCurrency, setWorkspaceApprovalMode, setWorkspaceAutoHarvesting, - setWorkspaceAutoReportingFrequency, setWorkspaceReimbursement, updateGeneralSettings, } from '@libs/actions/Policy/Policy'; @@ -175,11 +174,7 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { onToggle: (isEnabled: boolean) => (policy ? setWorkspaceAutoHarvesting(policy, isEnabled) : undefined), subMenuItems: ( Date: Sun, 14 Sep 2025 21:45:18 +0100 Subject: [PATCH 29/50] spell fix --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 6d8b740390074..46c04b3b82236 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -253,13 +253,13 @@ function IOURequestStepParticipants({ } const newReportID = selectedReportID.current; - const shouldUpdateTransactipnReportID = participants?.at(0)?.reportID !== newReportID; - const transactipnReportID = shouldAutoReport.current ? newReportID : CONST.REPORT.UNREPORTED_REPORT_ID; + const shouldUpdateTransactionReportID = participants?.at(0)?.reportID !== newReportID; + const transactionReportID = shouldAutoReport.current ? newReportID : CONST.REPORT.UNREPORTED_REPORT_ID; transactions.forEach((transaction) => { setMoneyRequestTag(transaction.transactionID, ''); setMoneyRequestCategory(transaction.transactionID, ''); - if (shouldUpdateTransactipnReportID) { - setTransactionReport(transaction.transactionID, {reportID: transactipnReportID}, true); + if (shouldUpdateTransactionReportID) { + setTransactionReport(transaction.transactionID, {reportID: transactionReportID}, true); } }); if ((isCategorizing || isShareAction) && numberOfParticipants.current === 0) { From 9375448f081becb57bb73f16943e78efc0f95f63 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:14:17 +0100 Subject: [PATCH 30/50] lint --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 1 + src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 98e41488a69f5..9629fa5e78763 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -967,6 +967,7 @@ function IOURequestStepConfirmation({ userLocation, submitPerDiemExpense, existingInvoiceReport, + isUnreported, ], ); diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index ffe7e22a5cd6e..03b0191bcfbbc 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -54,7 +54,6 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {ToggleSettingOptionRowProps} from './ToggleSettingsOptionRow'; import ToggleSettingOptionRow from './ToggleSettingsOptionRow'; -import type {AutoReportingFrequencyKey} from './WorkspaceAutoReportingFrequencyPage'; import {getAutoReportingFrequencyDisplayNames} from './WorkspaceAutoReportingFrequencyPage'; type WorkspaceWorkflowsPageProps = WithPolicyProps & PlatformStackScreenProps; From fba7b71c647f40c2309eb580f8e52b7b7cb083ed Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:18:04 +0100 Subject: [PATCH 31/50] lint --- .../MoneyRequestConfirmationListFooter.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 04a62bc5beee5..4f1183c1686be 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -18,17 +18,7 @@ import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getT import {canSendInvoice, getPerDiemCustomUnit} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; -import { - buildOptimisticExpenseReport, - generateReportID, - getDefaultWorkspaceAvatar, - getOutstandingReportsForUser, - getReportName, - isArchivedReport, - isMoneyRequestReport, - isReportOutstanding, - populateOptimisticReportFormula, -} from '@libs/ReportUtils'; +import {generateReportID, getDefaultWorkspaceAvatar, getOutstandingReportsForUser, getReportName, isArchivedReport, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; import {getTagVisibility, hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, From 5d72cf643095af89b9ba6d8c66ff273e2574c6f3 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:32:37 +0100 Subject: [PATCH 32/50] lint --- src/pages/workspace/duplicate/utils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/workspace/duplicate/utils.ts b/src/pages/workspace/duplicate/utils.ts index aa4a78b79e705..a672c22087c39 100644 --- a/src/pages/workspace/duplicate/utils.ts +++ b/src/pages/workspace/duplicate/utils.ts @@ -1,7 +1,6 @@ import type {LocaleContextProps} from '@components/LocaleContextProvider'; import {getCorrectedAutoReportingFrequency, getWorkflowApprovalsUnavailable, hasVBBA} from '@libs/PolicyUtils'; import {getAutoReportingFrequencyDisplayNames} from '@pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage'; -import type {AutoReportingFrequencyKey} from '@pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage'; import {isAuthenticationError} from '@userActions/connections'; import CONST from '@src/CONST'; import type {Policy} from '@src/types/onyx'; @@ -53,8 +52,7 @@ function getWorkflowRules(policy: Policy | undefined, translate: LocaleContextPr const shouldShowBankAccount = !!bankAccountID && policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES; if (policy?.autoReportingFrequency !== CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT && !hasDelayedSubmissionError) { - const title = - getAutoReportingFrequencyDisplayNames(translate)[(getCorrectedAutoReportingFrequency(policy) as AutoReportingFrequencyKey) ?? CONST.POLICY.AUTO_REPORTING_FREQUENCIES.WEEKLY]; + const title = getAutoReportingFrequencyDisplayNames(translate)[getCorrectedAutoReportingFrequency(policy) ?? CONST.POLICY.AUTO_REPORTING_FREQUENCIES.WEEKLY]; total.push(`${title} ${translate('workspace.duplicateWorkspace.delayedSubmission')}`); } if ([CONST.POLICY.APPROVAL_MODE.BASIC, CONST.POLICY.APPROVAL_MODE.ADVANCED].some((approvalMode) => approvalMode === policy?.approvalMode) && !hasApprovalError) { From 00eb8d77eea90eaef034116e50666bbb882192d4 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Tue, 16 Sep 2025 00:43:51 +0100 Subject: [PATCH 33/50] update translation --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 526ad802e07dd..5ea9d4705fd36 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -648,7 +648,7 @@ const translations = { enableGlobalReimbursements: 'Habilitar Reembolsos Globales', purchaseAmount: 'Importe de compra', frequency: 'Frecuencia', - newReport: 'Nuevo informe', + newReport: 'Informe nuevo', }, supportalNoAccess: { title: 'No tan rápido', From eee81705e5df6f10cd006574ae4863752273201b Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Tue, 16 Sep 2025 21:35:50 +0100 Subject: [PATCH 34/50] optimisticaly build self dm report --- .../request/step/IOURequestStepConfirmation.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 4f881836706cd..f88f93bd180ef 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -42,7 +42,7 @@ import {rand64} from '@libs/NumberUtils'; import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; import {isPaidGroupPolicy} from '@libs/PolicyUtils'; -import {findSelfDMReportID, generateReportID, getReportOrDraftReport, isProcessingReport, isReportOutstanding, isSelectedManagerMcTest} from '@libs/ReportUtils'; +import {buildOptimisticChatReport, findSelfDMReportID, generateReportID, getReportOrDraftReport, isProcessingReport, isReportOutstanding, isSelectedManagerMcTest} from '@libs/ReportUtils'; import { getAttendees, getDefaultTaxCode, @@ -168,13 +168,23 @@ function IOURequestStepConfirmation({ transactionReport && !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, undefined, false); const report = useMemo(() => { if (isUnreported) { - return selfDMReport; + // If no selfDM report is found we use an optimistic one with reportID = 0 + // By defaults tracking into reportID = 0 will track into the selfDM report and proactively creates it + // s77rt TODO: Generate random report ID and have BE use it instead + use buildOptimisticSelfDMReport + return ( + selfDMReport ?? + buildOptimisticChatReport({ + participantList: [currentUserPersonalDetails.accountID], + chatType: CONST.REPORT.CHAT_TYPE.SELF_DM, + optimisticReportID: CONST.REPORT.UNREPORTED_REPORT_ID, + }) + ); } if (shouldUseTransactionReport) { return transactionReport; } return reportReal ?? reportDraft; - }, [isUnreported, selfDMReport, shouldUseTransactionReport, transactionReport, reportReal, reportDraft]); + }, [isUnreported, selfDMReport, shouldUseTransactionReport, transactionReport, reportReal, reportDraft, currentUserPersonalDetails.accountID]); const policy = policyReal ?? policyDraft; const policyID = isUnreported ? policy?.id : getIOURequestPolicyID(transaction, report); const isDraftPolicy = policy === policyDraft; From 3c6151b51146bc8acee530e6e7d04e45bef4c6e4 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Sep 2025 00:37:29 +0100 Subject: [PATCH 35/50] add comment --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 46c04b3b82236..89db386b5065f 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -237,6 +237,8 @@ function IOURequestStepParticipants({ // We use || to be sure that if the first participant doesn't have a reportID, we generate a new one. // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing selectedReportID.current = firstParticipantReportID || generateReportID(); + + // IOUs are always reported shouldAutoReport.current = !isPolicyExpenseChat || !!policy?.autoReporting || !!personalPolicy?.autoReporting; }, [iouType, transactions, isMovingTransactionFromTrackExpense, reportID, trackExpense, allPolicies, personalPolicy, lastSelectedDistanceRates], From ea74d865189a456a54f0c49cd07f0cb67507f9d8 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Sep 2025 00:39:40 +0100 Subject: [PATCH 36/50] use usePersonalPolicy --- src/pages/iou/request/step/IOURequestStepAmount.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index d4d872403ac0a..2c1686b4b1bd8 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -8,6 +8,7 @@ import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactio import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePersonalPolicy from '@hooks/usePersonalPolicy'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; import {setTransactionReport} from '@libs/actions/Transaction'; import {createDraftTransaction, removeDraftTransaction} from '@libs/actions/TransactionEdit'; @@ -101,8 +102,7 @@ function IOURequestStepAmount({ const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true}); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true}); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); - const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: (policies) => mapOnyxCollectionItems(policies, policySelector), canBeMissing: true}); - const personalPolicy = useMemo(() => Object.values(allPolicies ?? {}).find((p) => p?.type === CONST.POLICY.TYPE.PERSONAL), [allPolicies]); + const personalPolicy = usePersonalPolicy(); const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(transactionID ? [transactionID] : []); const [reportAttributesDerived] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {canBeMissing: true, selector: (val) => val?.reports}); const isEditing = action === CONST.IOU.ACTION.EDIT; From 562ab2e41522d9666c7fec99fde8230d9817a4ea Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:23:45 +0100 Subject: [PATCH 37/50] allow passing undefined report in TrackExpense --- src/libs/actions/IOU.ts | 8 ++++---- .../request/step/IOURequestStepConfirmation.tsx | 14 ++------------ 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index d53b47f08cbbd..7c1b6f136a61c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -595,7 +595,7 @@ type TrackExpenseAccountantParams = { }; type CreateTrackExpenseParams = { - report: OnyxTypes.Report; + report: OnyxTypes.Report | undefined; isDraftPolicy: boolean; action?: IOUAction; participantParams: RequestMoneyParticipantParams; @@ -5999,8 +5999,8 @@ function trackExpense(params: CreateTrackExpenseParams) { } = transactionData; const isMoneyRequestReport = isMoneyRequestReportReportUtils(report); - const currentChatReport = isMoneyRequestReport ? getReportOrDraftReport(report.chatReportID) : report; - const moneyRequestReportID = isMoneyRequestReport ? report.reportID : ''; + const currentChatReport = isMoneyRequestReport ? getReportOrDraftReport(report?.chatReportID) : report; + const moneyRequestReportID = isMoneyRequestReport ? report?.reportID : ''; const isMovingTransactionFromTrackExpense = isMovingTransactionFromTrackExpenseIOUUtils(action); // Pass an open receipt so the distance expense will show a map with the route optimistically @@ -6087,7 +6087,7 @@ function trackExpense(params: CreateTrackExpenseParams) { }, retryParams, }) ?? {}; - const activeReportID = isMoneyRequestReport ? report.reportID : chatReport?.reportID; + const activeReportID = isMoneyRequestReport ? report?.reportID : chatReport?.reportID; const recentServerValidatedWaypoints = getRecentWaypoints().filter((item) => !item.pendingAction); onyxData?.failureData?.push({ diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index f88f93bd180ef..f536da65886c3 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -168,17 +168,7 @@ function IOURequestStepConfirmation({ transactionReport && !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, undefined, false); const report = useMemo(() => { if (isUnreported) { - // If no selfDM report is found we use an optimistic one with reportID = 0 - // By defaults tracking into reportID = 0 will track into the selfDM report and proactively creates it - // s77rt TODO: Generate random report ID and have BE use it instead + use buildOptimisticSelfDMReport - return ( - selfDMReport ?? - buildOptimisticChatReport({ - participantList: [currentUserPersonalDetails.accountID], - chatType: CONST.REPORT.CHAT_TYPE.SELF_DM, - optimisticReportID: CONST.REPORT.UNREPORTED_REPORT_ID, - }) - ); + return undefined; } if (shouldUseTransactionReport) { return transactionReport; @@ -613,7 +603,7 @@ function IOURequestStepConfirmation({ const trackExpense = useCallback( (selectedParticipants: Participant[], gpsPoints?: GpsPoint) => { - if (!report || !transactions.length) { + if (!transactions.length) { return; } const participant = selectedParticipants.at(0); From af7488989ddec49cd260fd19f9017e75662a924d Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:50:16 +0100 Subject: [PATCH 38/50] better type --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7c1b6f136a61c..cdcb00cbc09a9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -595,7 +595,7 @@ type TrackExpenseAccountantParams = { }; type CreateTrackExpenseParams = { - report: OnyxTypes.Report | undefined; + report: OnyxEntry; isDraftPolicy: boolean; action?: IOUAction; participantParams: RequestMoneyParticipantParams; From 41a9c018827356f72c433aad0aae56406ba34c4b Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:02:30 +0100 Subject: [PATCH 39/50] trackExpense default to self-DM if no report is passed --- src/libs/actions/IOU.ts | 16 +++++++++++----- .../request/step/IOURequestStepConfirmation.tsx | 7 ++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index cdcb00cbc09a9..be1c48c5c32e8 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -136,6 +136,7 @@ import { buildOptimisticReportPreview, buildOptimisticResolvedDuplicatesReportAction, buildOptimisticRetractedReportAction, + buildOptimisticSelfDMReport, buildOptimisticSubmittedReportAction, buildOptimisticUnapprovedReportAction, buildOptimisticUnHoldReportAction, @@ -3880,15 +3881,20 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T // STEP 1: Get existing chat report let chatReport = !isEmptyObject(parentChatReport) && parentChatReport?.reportID ? parentChatReport : null; - // The chatReport always exists, and we can get it from Onyx if chatReport is null. + + // If no chat report is passed, defaults to the self-DM report if (!chatReport) { - chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.reportID}`] ?? null; + const selfDMReportID = findSelfDMReportID(); + chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`] ?? null; } - // If we still don't have a report, it likely doesn't exist, and we will early return here as it should not happen - // Maybe later, we can build an optimistic selfDM chat. + // If we are still missing the chat report then optimistically create the self-DM report if (!chatReport) { - return null; + const currentTime = DateUtils.getDBTime(); + const selfDMReport = buildOptimisticSelfDMReport(currentTime); + const selfDMCreatedReportAction = buildOptimisticCreatedReportAction(currentUserEmail ?? '', currentTime); + chatReport = selfDMReport; + // s77rt TODO: handle onyx data } // Check if the report is a draft diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index f536da65886c3..7668976fa3710 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -42,7 +42,7 @@ import {rand64} from '@libs/NumberUtils'; import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; import {isPaidGroupPolicy} from '@libs/PolicyUtils'; -import {buildOptimisticChatReport, findSelfDMReportID, generateReportID, getReportOrDraftReport, isProcessingReport, isReportOutstanding, isSelectedManagerMcTest} from '@libs/ReportUtils'; +import {generateReportID, getReportOrDraftReport, isProcessingReport, isReportOutstanding, isSelectedManagerMcTest} from '@libs/ReportUtils'; import { getAttendees, getDefaultTaxCode, @@ -154,13 +154,10 @@ function IOURequestStepConfirmation({ 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 [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); - const selfDMReportID = useMemo(() => findSelfDMReportID(), []); - const [selfDMReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, {canBeMissing: true}); /* * We want to use a report from the transaction if it exists * Also if the report was submitted and delayed submission is on, then we should use an initial report - * If this is an unreported expense then use the self DM as home of unreported expenses */ const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = getReportOrDraftReport(transaction?.reportID); @@ -174,7 +171,7 @@ function IOURequestStepConfirmation({ return transactionReport; } return reportReal ?? reportDraft; - }, [isUnreported, selfDMReport, shouldUseTransactionReport, transactionReport, reportReal, reportDraft, currentUserPersonalDetails.accountID]); + }, [isUnreported, shouldUseTransactionReport, transactionReport, reportReal, reportDraft]); const policy = policyReal ?? policyDraft; const policyID = isUnreported ? policy?.id : getIOURequestPolicyID(transaction, report); const isDraftPolicy = policy === policyDraft; From cea1f233c063ab02e86c620729fd7ee8c12d27af Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Sep 2025 13:56:00 +0100 Subject: [PATCH 40/50] pass selfDMReportID and selfDMCreatedReportActionID to TrackExpense command --- src/libs/API/parameters/TrackExpenseParams.ts | 2 ++ src/libs/actions/IOU.ts | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/libs/API/parameters/TrackExpenseParams.ts b/src/libs/API/parameters/TrackExpenseParams.ts index b66f45e5b1044..b9aa6f7ba0d3a 100644 --- a/src/libs/API/parameters/TrackExpenseParams.ts +++ b/src/libs/API/parameters/TrackExpenseParams.ts @@ -15,6 +15,8 @@ type TrackExpenseParams = { createdChatReportActionID?: string; createdIOUReportActionID?: string; reportPreviewReportActionID?: string; + selfDMReportID?: string; + selfDMCreatedReportActionID?: string; receipt?: Receipt; receiptState?: ValueOf; category?: string; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index be1c48c5c32e8..0d25a9b010da8 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -315,6 +315,8 @@ type TrackExpenseInformation = { transactionThreadReportID: string; createdReportActionIDForThread: string | undefined; actionableWhisperReportActionIDParam?: string; + selfDMReportID: string | undefined; + selfDMCreatedReportActionID: string | undefined; onyxData: OnyxData; }; @@ -3884,17 +3886,22 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T // If no chat report is passed, defaults to the self-DM report if (!chatReport) { - const selfDMReportID = findSelfDMReportID(); - chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`] ?? null; + chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${findSelfDMReportID()}`] ?? null; } // If we are still missing the chat report then optimistically create the self-DM report + let selfDMReportID: string | undefined; + let selfDMCreatedReportActionID: string | undefined; if (!chatReport) { const currentTime = DateUtils.getDBTime(); const selfDMReport = buildOptimisticSelfDMReport(currentTime); const selfDMCreatedReportAction = buildOptimisticCreatedReportAction(currentUserEmail ?? '', currentTime); - chatReport = selfDMReport; + // s77rt TODO: handle onyx data + + selfDMReportID = selfDMReport.reportID; + selfDMCreatedReportActionID = selfDMCreatedReportAction.reportActionID; + chatReport = selfDMReport; } // Check if the report is a draft @@ -4067,6 +4074,8 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T transactionThreadReportID: optimisticTransactionThread.reportID, createdReportActionIDForThread: optimisticCreatedActionForTransactionThread?.reportActionID, actionableWhisperReportActionIDParam: actionableTrackExpenseWhisper?.reportActionID, + selfDMReportID, + selfDMCreatedReportActionID, onyxData: { optimisticData: optimisticData.concat(trackExpenseOnyxData[0]), successData: successData.concat(trackExpenseOnyxData[1]), @@ -6056,6 +6065,8 @@ function trackExpense(params: CreateTrackExpenseParams) { transactionThreadReportID, createdReportActionIDForThread, actionableWhisperReportActionIDParam, + selfDMReportID, + selfDMCreatedReportActionID, onyxData, } = getTrackExpenseInformation({ @@ -6220,6 +6231,8 @@ function trackExpense(params: CreateTrackExpenseParams) { createdChatReportActionID, createdIOUReportActionID, reportPreviewReportActionID: reportPreviewAction?.reportActionID, + selfDMReportID, + selfDMCreatedReportActionID, receipt: isFileUploadable(trackedReceipt) ? trackedReceipt : undefined, receiptState: trackedReceipt?.state, category, From 6552d5adfa8b72b38c154b41a71dd1aa49296554 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:51:34 +0100 Subject: [PATCH 41/50] don't pass chatReportID if we are passing selfDMReportID to TrackExpense --- src/libs/actions/IOU.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0d25a9b010da8..b70aaf924b38a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6225,7 +6225,9 @@ function trackExpense(params: CreateTrackExpenseParams) { created, merchant, iouReportID: iouReport?.reportID, - chatReportID: chatReport?.reportID, + // If we are passing selfDMReportID then we shouldn't pass chatReportID as it masks selfDMReportID + // i.e. BE fallback to selfDMReportID only if no chatReportID is passed + chatReportID: selfDMReportID ? undefined : chatReport?.reportID, transactionID: transaction?.transactionID, reportActionID: iouAction?.reportActionID, createdChatReportActionID, From e8be22217b2eaf91c162d9563d86f31459914831 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:59:51 +0100 Subject: [PATCH 42/50] add optimistic onyx data for created self dm report --- src/libs/actions/IOU.ts | 72 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b70aaf924b38a..be722eb4a66b0 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3889,19 +3889,83 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${findSelfDMReportID()}`] ?? null; } - // If we are still missing the chat report then optimistically create the self-DM report + // If we are still missing the chat report then optimistically create the self-DM report and use it let selfDMReportID: string | undefined; let selfDMCreatedReportActionID: string | undefined; if (!chatReport) { const currentTime = DateUtils.getDBTime(); const selfDMReport = buildOptimisticSelfDMReport(currentTime); const selfDMCreatedReportAction = buildOptimisticCreatedReportAction(currentUserEmail ?? '', currentTime); - - // s77rt TODO: handle onyx data - selfDMReportID = selfDMReport.reportID; selfDMCreatedReportActionID = selfDMCreatedReportAction.reportActionID; chatReport = selfDMReport; + + optimisticData.push( + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, + value: { + ...selfDMReport, + pendingFields: { + createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${selfDMReportID}`, + value: {isOptimisticReport: true}, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${selfDMReportID}`, + value: { + [selfDMCreatedReportActionID]: selfDMCreatedReportAction, + }, + }, + ); + successData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, + value: { + pendingFields: { + createChat: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${selfDMReportID}`, + value: {isOptimisticReport: false}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${selfDMReportID}`, + value: { + [selfDMCreatedReportActionID]: { + pendingAction: null, + }, + }, + }, + ); + failureData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${selfDMReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${selfDMReportID}`, + value: null, + }, + ); } // Check if the report is a draft From 3197216ba945480ce9eeb52e0f7819366cc26203 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 24 Sep 2025 14:23:12 +0100 Subject: [PATCH 43/50] TrackExpense: rename selfDMReportID/selfDMCreatedReportActionID to optimisticReportID/optimisticReportActionID --- src/libs/API/parameters/TrackExpenseParams.ts | 4 +- .../Navigation/AppNavigator/AuthScreens.tsx | 2 +- src/libs/actions/IOU.ts | 51 +++++++++---------- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/libs/API/parameters/TrackExpenseParams.ts b/src/libs/API/parameters/TrackExpenseParams.ts index b9aa6f7ba0d3a..7cb8fb86a9ec2 100644 --- a/src/libs/API/parameters/TrackExpenseParams.ts +++ b/src/libs/API/parameters/TrackExpenseParams.ts @@ -15,8 +15,8 @@ type TrackExpenseParams = { createdChatReportActionID?: string; createdIOUReportActionID?: string; reportPreviewReportActionID?: string; - selfDMReportID?: string; - selfDMCreatedReportActionID?: string; + optimisticReportID?: string; + optimisticReportActionID?: string; receipt?: Receipt; receiptState?: ValueOf; category?: string; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 634ee0b292c94..d1477577de6a1 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -223,7 +223,7 @@ function AuthScreens() { const [onboardingCompanySize] = useOnyx(ONYXKEYS.ONBOARDING_COMPANY_SIZE, {canBeMissing: true}); const [userReportedIntegration] = useOnyx(ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION, {canBeMissing: true}); const modal = useRef({}); - const {isOnboardingCompleted} = useOnboardingFlowRouter(); + const isOnboardingCompleted = true; const [isOnboardingLoading] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {canBeMissing: true, selector: (value) => !!value?.isLoading}); const prevIsOnboardingLoading = usePrevious(isOnboardingLoading); const [shouldShowRequire2FAPage, setShouldShowRequire2FAPage] = useState(!!account?.needsTwoFactorAuthSetup && !account.requiresTwoFactorAuth); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index eb6a7024d3a05..e800117be9522 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -319,8 +319,8 @@ type TrackExpenseInformation = { transactionThreadReportID: string; createdReportActionIDForThread: string | undefined; actionableWhisperReportActionIDParam?: string; - selfDMReportID: string | undefined; - selfDMCreatedReportActionID: string | undefined; + optimisticReportID: string | undefined; + optimisticReportActionID: string | undefined; onyxData: OnyxData; }; @@ -3903,20 +3903,20 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T } // If we are still missing the chat report then optimistically create the self-DM report and use it - let selfDMReportID: string | undefined; - let selfDMCreatedReportActionID: string | undefined; + let optimisticReportID: string | undefined; + let optimisticReportActionID: string | undefined; if (!chatReport) { const currentTime = DateUtils.getDBTime(); const selfDMReport = buildOptimisticSelfDMReport(currentTime); const selfDMCreatedReportAction = buildOptimisticCreatedReportAction(currentUserEmail ?? '', currentTime); - selfDMReportID = selfDMReport.reportID; - selfDMCreatedReportActionID = selfDMCreatedReportAction.reportActionID; + optimisticReportID = selfDMReport.reportID; + optimisticReportActionID = selfDMCreatedReportAction.reportActionID; chatReport = selfDMReport; optimisticData.push( { onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticReportID}`, value: { ...selfDMReport, pendingFields: { @@ -3926,21 +3926,21 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${selfDMReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${optimisticReportID}`, value: {isOptimisticReport: true}, }, { onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${selfDMReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticReportID}`, value: { - [selfDMCreatedReportActionID]: selfDMCreatedReportAction, + [optimisticReportActionID]: selfDMCreatedReportAction, }, }, ); successData.push( { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticReportID}`, value: { pendingFields: { createChat: null, @@ -3949,14 +3949,14 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${selfDMReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${optimisticReportID}`, value: {isOptimisticReport: false}, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${selfDMReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticReportID}`, value: { - [selfDMCreatedReportActionID]: { + [optimisticReportActionID]: { pendingAction: null, }, }, @@ -3965,17 +3965,17 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T failureData.push( { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticReportID}`, value: null, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${selfDMReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${optimisticReportID}`, value: null, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${selfDMReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticReportID}`, value: null, }, ); @@ -4151,8 +4151,8 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T transactionThreadReportID: optimisticTransactionThread.reportID, createdReportActionIDForThread: optimisticCreatedActionForTransactionThread?.reportActionID, actionableWhisperReportActionIDParam: actionableTrackExpenseWhisper?.reportActionID, - selfDMReportID, - selfDMCreatedReportActionID, + optimisticReportID, + optimisticReportActionID, onyxData: { optimisticData: optimisticData.concat(trackExpenseOnyxData[0]), successData: successData.concat(trackExpenseOnyxData[1]), @@ -6153,8 +6153,8 @@ function trackExpense(params: CreateTrackExpenseParams) { transactionThreadReportID, createdReportActionIDForThread, actionableWhisperReportActionIDParam, - selfDMReportID, - selfDMCreatedReportActionID, + optimisticReportID, + optimisticReportActionID, onyxData, } = getTrackExpenseInformation({ @@ -6321,16 +6321,15 @@ function trackExpense(params: CreateTrackExpenseParams) { created, merchant, iouReportID: iouReport?.reportID, - // If we are passing selfDMReportID then we shouldn't pass chatReportID as it masks selfDMReportID - // i.e. BE fallback to selfDMReportID only if no chatReportID is passed - chatReportID: selfDMReportID ? undefined : chatReport?.reportID, + // If we are passing an optimisticReportID then we are creating a new chat (selfDM) and we don't have an *existing* chatReportID + chatReportID: optimisticReportID ? undefined : chatReport?.reportID, transactionID: transaction?.transactionID, reportActionID: iouAction?.reportActionID, createdChatReportActionID, createdIOUReportActionID, reportPreviewReportActionID: reportPreviewAction?.reportActionID, - selfDMReportID, - selfDMCreatedReportActionID, + optimisticReportID, + optimisticReportActionID, receipt: isFileUploadable(trackedReceipt) ? trackedReceipt : undefined, receiptState: trackedReceipt?.state, category, From 6ee3b069490db198a416379ec857f40782c7acec Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 24 Sep 2025 14:25:23 +0100 Subject: [PATCH 44/50] revert unintentional change --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index d1477577de6a1..634ee0b292c94 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -223,7 +223,7 @@ function AuthScreens() { const [onboardingCompanySize] = useOnyx(ONYXKEYS.ONBOARDING_COMPANY_SIZE, {canBeMissing: true}); const [userReportedIntegration] = useOnyx(ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION, {canBeMissing: true}); const modal = useRef({}); - const isOnboardingCompleted = true; + const {isOnboardingCompleted} = useOnboardingFlowRouter(); const [isOnboardingLoading] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {canBeMissing: true, selector: (value) => !!value?.isLoading}); const prevIsOnboardingLoading = usePrevious(isOnboardingLoading); const [shouldShowRequire2FAPage, setShouldShowRequire2FAPage] = useState(!!account?.needsTwoFactorAuthSetup && !account.requiresTwoFactorAuth); From 40700697619bd2e42ab0c92fd3e72eb6d1723a5f Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:22:55 +0100 Subject: [PATCH 45/50] clean up and fix handling preexistingreport pattern --- src/libs/ReportUtils.ts | 7 +++- src/libs/actions/Report.ts | 85 ++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index dee9dcd46fcdd..0938d63a16ef4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -8,6 +8,7 @@ import isEmpty from 'lodash/isEmpty'; import isNumber from 'lodash/isNumber'; import mapValues from 'lodash/mapValues'; import lodashMaxBy from 'lodash/maxBy'; +import {InteractionManager} from 'react-native'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {SvgProps} from 'react-native-svg'; @@ -86,7 +87,7 @@ import { import {isApprover as isApproverUtils} from './actions/Policy/Member'; import {createDraftWorkspace} from './actions/Policy/Policy'; import {hasCreditBankAccount} from './actions/ReimbursementAccount/store'; -import {handleReportChanged, openUnreportedExpense} from './actions/Report'; +import {handlePreexistingReport, openUnreportedExpense} from './actions/Report'; import type {GuidedSetupData, TaskForParameters} from './actions/Report'; import {isAnonymousUser as isAnonymousUserSession} from './actions/Session'; import {getOnboardingMessages} from './actions/Welcome/OnboardingFlow'; @@ -998,7 +999,9 @@ Onyx.connect({ return acc; } - handleReportChanged(report); + InteractionManager.runAfterInteractions(() => { + handlePreexistingReport(report); + }); // Get all reports, which are the ones that are: // - Owned by the same user diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index ed7a9ee4c1885..a0b011a60c24b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1806,16 +1806,15 @@ function broadcastUserIsLeavingRoom(reportID: string) { Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); } -/** When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name */ -function handleReportChanged(report: OnyxEntry) { - if (!report) { +function handlePreexistingReport(report: Report) { + const {reportID, preexistingReportID, parentReportID, parentReportActionID} = report; + + if (!reportID || !preexistingReportID) { return; } - const {reportID, preexistingReportID, parentReportID, parentReportActionID} = report; - // Handle cleanup of stale optimistic IOU report and its report preview separately - if (reportID && preexistingReportID && isMoneyRequestReport(report) && parentReportActionID) { + if (isMoneyRequestReport(report) && parentReportActionID) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, { [parentReportActionID]: null, }); @@ -1826,48 +1825,46 @@ function handleReportChanged(report: OnyxEntry) { // It is possible that we optimistically created a DM/group-DM for a set of users for which a report already exists. // In this case, the API will let us know by returning a preexistingReportID. // We should clear out the optimistically created report and re-route the user to the preexisting report. - if (reportID && preexistingReportID) { - let callback = () => { - const existingReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${preexistingReportID}`]; - - Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, null); - Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${preexistingReportID}`, { - ...report, - reportID: preexistingReportID, - preexistingReportID: null, - // Replacing the existing report's participants to avoid duplicates - participants: existingReport?.participants ?? report.participants, - }); - Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, null); + let callback = () => { + const existingReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${preexistingReportID}`]; + + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, null); + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${preexistingReportID}`, { + ...report, + reportID: preexistingReportID, + preexistingReportID: null, + // Replacing the existing report's participants to avoid duplicates + participants: existingReport?.participants ?? report.participants, + }); + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, null); + }; + // Only re-route them if they are still looking at the optimistically created report + if (Navigation.getActiveRoute().includes(`/r/${reportID}`)) { + const currCallback = callback; + callback = () => { + currCallback(); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(preexistingReportID), {forceReplace: true}); }; - // Only re-route them if they are still looking at the optimistically created report - if (Navigation.getActiveRoute().includes(`/r/${reportID}`)) { - const currCallback = callback; - callback = () => { - currCallback(); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(preexistingReportID), {forceReplace: true}); - }; - - // The report screen will listen to this event and transfer the draft comment to the existing report - // This will allow the newest draft comment to be transferred to the existing report - DeviceEventEmitter.emit(`switchToPreExistingReport_${reportID}`, { - preexistingReportID, - callback, - }); - return; - } + // The report screen will listen to this event and transfer the draft comment to the existing report + // This will allow the newest draft comment to be transferred to the existing report + DeviceEventEmitter.emit(`switchToPreExistingReport_${reportID}`, { + preexistingReportID, + callback, + }); - // In case the user is not on the report screen, we will transfer the report draft comment directly to the existing report - // after that clear the optimistically created report - const draftReportComment = allReportDraftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`]; - if (!draftReportComment) { - callback(); - return; - } + return; + } - saveReportDraftComment(preexistingReportID, draftReportComment, callback); + // In case the user is not on the report screen, we will transfer the report draft comment directly to the existing report + // after that clear the optimistically created report + const draftReportComment = allReportDraftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`]; + if (!draftReportComment) { + callback(); + return; } + + saveReportDraftComment(preexistingReportID, draftReportComment, callback); } /** Deletes a comment from the report, basically sets it as empty string */ @@ -6119,7 +6116,7 @@ export { getNewerActions, getOlderActions, getReportPrivateNote, - handleReportChanged, + handlePreexistingReport, handleUserDeletedLinksInHtml, hasErrorInPrivateNotes, inviteToGroupChat, From 87e714b9b6abcae6fba5658b032d5475b43af9f1 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:46:21 +0100 Subject: [PATCH 46/50] revert report change logic --- src/pages/iou/request/step/IOURequestEditReportCommon.tsx | 2 +- src/pages/iou/request/step/IOURequestStepReport.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 1913a7cfbb531..7d1361b5aeef3 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -80,7 +80,7 @@ function IOURequestEditReportCommon({ .some((transaction) => transaction?.comment?.liabilityType === CONST.TRANSACTION.LIABILITY_TYPE.RESTRICT); }, [transactionIDs, selectedReport, reportTransactions]); - const shouldShowRemoveFromReport = isOwner && !isReportIOU && !isUnreported && !isCardTransaction; + const shouldShowRemoveFromReport = isEditing && isOwner && !isReportIOU && !isUnreported && !isCardTransaction; const expenseReports = useMemo(() => { // Early return if no reports are available to prevent useless loop diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 042fc9f18597a..5fb9ef76b2032 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -153,6 +153,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { session?.accountID ?? CONST.DEFAULT_NUMBER_ID, session?.email ?? '', ); + removeTransaction(transaction.transactionID); }); }; From e59e005f1caaf318d3e7ca2a3c67944dc2d2861a Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:53:10 +0100 Subject: [PATCH 47/50] ts + lint --- .../MoneyRequestConfirmationListFooter.tsx | 1 - src/pages/iou/request/step/IOURequestStepAmount.tsx | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 57a8a6719696c..d21a1ba7627ce 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -234,7 +234,6 @@ function MoneyRequestConfirmationListFooter({ onToggleBillable, policy, policyTags, - rawAmount, policyTagLists, rate, receiptFilename, diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index e5313c27bd203..baa55ffc66243 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -46,24 +46,11 @@ import type {Policy, SelectedTabRequest} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type Transaction from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; -const policySelector = (policy: OnyxEntry): OnyxEntry => - policy && { - id: policy.id, - name: policy.name, - type: policy.type, - role: policy.role, - owner: policy.owner, - outputCurrency: policy.outputCurrency, - isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, - autoReporting: policy.autoReporting, - }; - type AmountParams = { amount: string; paymentMethod?: PaymentMethodType; From cb9dde5d1551f676d0aaca335a65c9b01cafbcd9 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:14:23 +0100 Subject: [PATCH 48/50] lint --- src/pages/iou/request/step/IOURequestStepAmount.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index baa55ffc66243..b8ba9b7e17d46 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -42,7 +42,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {Policy, SelectedTabRequest} from '@src/types/onyx'; +import type {SelectedTabRequest} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type Transaction from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; From 3080a2bace1ad23168b946857104830dbf6414bc Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:06:58 +0100 Subject: [PATCH 49/50] non-CREATE actions require a report --- .../iou/request/step/IOURequestStepDestination.tsx | 2 +- .../iou/request/step/IOURequestStepParticipants.tsx | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepDestination.tsx b/src/pages/iou/request/step/IOURequestStepDestination.tsx index 4680ffbd2b272..626114e35e1fd 100644 --- a/src/pages/iou/request/step/IOURequestStepDestination.tsx +++ b/src/pages/iou/request/step/IOURequestStepDestination.tsx @@ -88,7 +88,7 @@ function IOURequestStepDestination({ if (openedFromStartPage) { // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldAutoReport = policy?.autoReporting || personalPolicy?.autoReporting; + const shouldAutoReport = policy?.autoReporting || personalPolicy?.autoReporting || action !== CONST.IOU.ACTION.CREATE; const transactionReportID = shouldAutoReport ? policyExpenseReport?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); setMoneyRequestParticipantsFromReport(transactionID, policyExpenseReport); diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 53d05fdc4d5ba..af8fd631fad7e 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -244,10 +244,14 @@ function IOURequestStepParticipants({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing selectedReportID.current = firstParticipantReportID || generateReportID(); - // IOUs are always reported - shouldAutoReport.current = !isPolicyExpenseChat || !!policy?.autoReporting || !!personalPolicy?.autoReporting; + // IOUs are always reported. non-CREATE actions require a report + if (!isPolicyExpenseChat || action !== CONST.IOU.ACTION.CREATE) { + shouldAutoReport.current = true; + } else { + shouldAutoReport.current = !!policy?.autoReporting || !!personalPolicy?.autoReporting; + } }, - [iouType, transactions, isMovingTransactionFromTrackExpense, reportID, trackExpense, allPolicies, personalPolicy, lastSelectedDistanceRates], + [action, iouType, transactions, isMovingTransactionFromTrackExpense, reportID, trackExpense, allPolicies, personalPolicy, lastSelectedDistanceRates], ); const goToNextStep = useCallback(() => { From 83c7a182045e451b36d9f8d08baa01ec64a5886f Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:09:13 +0100 Subject: [PATCH 50/50] fix lint --- src/pages/Search/SearchPage.tsx | 4 +--- src/pages/iou/request/step/IOURequestStepAmount.tsx | 4 +--- src/pages/iou/request/step/IOURequestStepDestination.tsx | 4 +--- src/pages/iou/request/step/IOURequestStepDistance.tsx | 4 +--- src/pages/iou/request/step/IOURequestStepDistanceManual.tsx | 4 +--- src/pages/iou/request/step/IOURequestStepDistanceMap.tsx | 4 +--- .../iou/request/step/IOURequestStepScan/index.native.tsx | 4 +--- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 4 +--- 8 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 4748d40b80e54..2e364c30f40ef 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -524,9 +524,7 @@ function SearchPage({route}: SearchPageProps) { if (isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); - // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldAutoReport = activePolicy?.autoReporting || personalPolicy?.autoReporting; + const shouldAutoReport = !!activePolicy?.autoReporting || !!personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; const setParticipantsPromises = newReceiptFiles.map((receiptFile) => { setTransactionReport(receiptFile.transactionID, {reportID: transactionReportID}, true); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index b8ba9b7e17d46..c7330485e92d9 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -263,9 +263,7 @@ function IOURequestStepAmount({ !shouldRestrictUserBillableActions(defaultExpensePolicy.id) ) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, defaultExpensePolicy?.id); - // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldAutoReport = defaultExpensePolicy?.autoReporting || personalPolicy?.autoReporting; + const shouldAutoReport = !!defaultExpensePolicy?.autoReporting || !!personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); setMoneyRequestParticipantsFromReport(transactionID, activePolicyExpenseChat).then(() => { diff --git a/src/pages/iou/request/step/IOURequestStepDestination.tsx b/src/pages/iou/request/step/IOURequestStepDestination.tsx index 626114e35e1fd..8123cf056c498 100644 --- a/src/pages/iou/request/step/IOURequestStepDestination.tsx +++ b/src/pages/iou/request/step/IOURequestStepDestination.tsx @@ -86,9 +86,7 @@ function IOURequestStepDestination({ } if (selectedDestination !== destination.keyForList) { if (openedFromStartPage) { - // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldAutoReport = policy?.autoReporting || personalPolicy?.autoReporting || action !== CONST.IOU.ACTION.CREATE; + const shouldAutoReport = !!policy?.autoReporting || !!personalPolicy?.autoReporting || action !== CONST.IOU.ACTION.CREATE; const transactionReportID = shouldAutoReport ? policyExpenseReport?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); setMoneyRequestParticipantsFromReport(transactionID, policyExpenseReport); diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 5ecb68f7fa5ca..00744e5940bbd 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -394,9 +394,7 @@ function IOURequestStepDistance({ policy: defaultExpensePolicy, lastSelectedDistanceRates, }); - // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldAutoReport = defaultExpensePolicy?.autoReporting || personalPolicy?.autoReporting; + const shouldAutoReport = !!defaultExpensePolicy?.autoReporting || !!personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); setCustomUnitRateID(transactionID, rateID); diff --git a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx index 5e2f65bec59d7..bf1302f721319 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx @@ -250,9 +250,7 @@ function IOURequestStepDistanceManual({ policy: defaultExpensePolicy, lastSelectedDistanceRates, }); - // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldAutoReport = defaultExpensePolicy?.autoReporting || personalPolicy?.autoReporting; + const shouldAutoReport = !!defaultExpensePolicy?.autoReporting || !!personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); setCustomUnitRateID(transactionID, rateID); diff --git a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx index d6eb2b76678dc..e599521040a5d 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx @@ -394,9 +394,7 @@ function IOURequestStepDistanceMap({ policy: defaultExpensePolicy, lastSelectedDistanceRates, }); - // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldAutoReport = defaultExpensePolicy?.autoReporting || personalPolicy?.autoReporting; + const shouldAutoReport = !!defaultExpensePolicy?.autoReporting || !!personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; setTransactionReport(transactionID, {reportID: transactionReportID}, true); setCustomUnitRateID(transactionID, rateID); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index a62d9f22a2d46..95bcd29d35647 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -472,9 +472,7 @@ function IOURequestStepScan({ !shouldRestrictUserBillableActions(defaultExpensePolicy.id) ) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, defaultExpensePolicy?.id); - // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldAutoReport = defaultExpensePolicy?.autoReporting || personalPolicy?.autoReporting; + const shouldAutoReport = !!defaultExpensePolicy?.autoReporting || !!personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; // If the initial transaction has different participants selected that means that the user has changed the participant in the confirmation step diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 90d1bd427977d..2b7252d995135 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -528,9 +528,7 @@ function IOURequestStepScan({ !shouldRestrictUserBillableActions(defaultExpensePolicy.id) ) { const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, defaultExpensePolicy?.id); - // We want to check both policies and fallback to personalPolicy.autoReporting if the former is false - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldAutoReport = defaultExpensePolicy?.autoReporting || personalPolicy?.autoReporting; + const shouldAutoReport = !!defaultExpensePolicy?.autoReporting || !!personalPolicy?.autoReporting; const transactionReportID = shouldAutoReport ? activePolicyExpenseChat?.reportID : CONST.REPORT.UNREPORTED_REPORT_ID; // If the initial transaction has different participants selected that means that the user has changed the participant in the confirmation step