From 291615979a2b28b8c6561d92f820f9c1bb305ce1 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 27 Jun 2026 12:02:27 +0800 Subject: [PATCH 1/5] pass the owner/submitter login from useOnyx --- .../ReportPreviewActionButton.tsx | 3 ++ src/hooks/useReportPrimaryAction.ts | 3 ++ src/hooks/useSelectionModeReportActions.ts | 1 + src/libs/OptionsListUtils/index.ts | 4 +- src/libs/PolicyUtils.ts | 27 ++++++------- src/libs/ReportPreviewActionUtils.ts | 7 +++- src/libs/ReportPrimaryActionUtils.ts | 7 +++- src/libs/ReportSecondaryActionUtils.ts | 6 ++- src/libs/ReportUtils.ts | 19 +++++---- src/libs/SearchUIUtils.ts | 4 +- src/libs/actions/OnyxDerived/configs/todos.ts | 21 +++++++++- src/libs/actions/Report/index.ts | 10 ++++- .../DynamicReportChangeWorkspacePage.tsx | 1 + tests/actions/IOUTest/ReportWorkflowTest.ts | 1 + tests/actions/ReportPreviewActionUtilsTest.ts | 28 +++++++++++++ tests/actions/ReportTest.ts | 11 +++++ tests/perf-test/PolicyUtils.perf-test.ts | 6 +-- tests/unit/NextStepUtilsTest.ts | 1 + tests/unit/PolicyUtilsTest.ts | 40 ++++++------------- tests/unit/ReportPrimaryActionUtilsTest.ts | 34 ++++++++++++++++ tests/unit/ReportUtilsTest.ts | 4 +- 21 files changed, 170 insertions(+), 68 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewActionButton.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewActionButton.tsx index 2b288702d3c7..3e94cdc4097b 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewActionButton.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewActionButton.tsx @@ -21,6 +21,7 @@ import variables from '@styles/variables'; import {canIOUBePaid as canIOUBePaidIOUActions} from '@userActions/IOU/ReportWorkflow'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import {personalDetailsLoginSelector} from '@src/selectors/PersonalDetails'; import {validTransactionDraftIDsSelector} from '@src/selectors/TransactionDraft'; import type {Report, Transaction} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; @@ -84,6 +85,7 @@ function ReportPreviewActionButton({ const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); const [iouReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${iouReportID}`); + const [ownerLogin] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: personalDetailsLoginSelector(iouReport?.ownerAccountID)}, [iouReport?.ownerAccountID]); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReportID}`); const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); @@ -141,6 +143,7 @@ function ReportPreviewActionButton({ isDEWSubmitPending, violationsData: transactionViolations, reportMetadata: iouReportMetadata, + ownerLogin, }); const renderButton = () => { diff --git a/src/hooks/useReportPrimaryAction.ts b/src/hooks/useReportPrimaryAction.ts index baba8f7a6193..79290bc79e51 100644 --- a/src/hooks/useReportPrimaryAction.ts +++ b/src/hooks/useReportPrimaryAction.ts @@ -8,6 +8,7 @@ import {isExpenseReport as isExpenseReportUtils} from '@libs/ReportUtils'; import {isTransactionPendingDelete} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import {personalDetailsLoginSelector} from '@src/selectors/PersonalDetails'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useOnyx from './useOnyx'; import useReportIsArchived from './useReportIsArchived'; @@ -23,6 +24,7 @@ function useReportPrimaryAction(reportID: string | undefined): ValueOf { diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 268d04936de7..282c3628b16e 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -1319,7 +1319,7 @@ function getReportOption( option.alternateText = translateLocal('workspace.common.workspace'); if (report?.policyID) { - const submitToAccountID = getSubmitToAccountID(policy, report); + const submitToAccountID = getSubmitToAccountID(policy, report, getLoginByAccountID(report?.ownerAccountID, personalDetails)); const submitsToAccountDetails = personalDetails?.[submitToAccountID]; const subtitle = submitsToAccountDetails?.displayName ?? submitsToAccountDetails?.login; @@ -2434,7 +2434,7 @@ function prepareReportOptionsForDisplay( newReportOption.alternateText = translateLocal('workspace.common.workspace'); if (report?.policyID) { - const submitToAccountID = getSubmitToAccountID(policy, report); + const submitToAccountID = getSubmitToAccountID(policy, report, getLoginByAccountID(report?.ownerAccountID, personalDetails)); const submitsToAccountDetails = personalDetails?.[submitToAccountID]; const subtitle = submitsToAccountDetails?.displayName ?? submitsToAccountDetails?.login; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 95b9836af362..d4259c348fff 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1536,28 +1536,30 @@ function getFirstRuleApprover(approvalRules: ApprovalRule[], expenseReport: Onyx return firstCategoryApprover || firstTagApprover; } -function getManagerAccountID(policy: OnyxEntry, expenseReport: OnyxEntry | {ownerAccountID: number}) { - const employeeAccountID = expenseReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID; - const employeeLogin = getLoginByAccountID(employeeAccountID) ?? ''; +function getManagerAccountEmail(policy: OnyxEntry, ownerLogin: string | undefined): string { const defaultApprover = getDefaultApprover(policy); // For policy using the optional or basic workflow, the manager is the policy default approver. if (([CONST.POLICY.APPROVAL_MODE.OPTIONAL, CONST.POLICY.APPROVAL_MODE.BASIC] as Array>).includes(getApprovalWorkflow(policy))) { - return getAccountIDsByLogins([defaultApprover]).at(0) ?? -1; + return defaultApprover; } - const employee = policy?.employeeList?.[employeeLogin]; + const employee = policy?.employeeList?.[ownerLogin ?? '']; if (!employee && !defaultApprover) { - return -1; + return ''; } - return getAccountIDsByLogins([employee?.submitsTo ?? defaultApprover]).at(0) ?? -1; + return employee?.submitsTo ?? defaultApprover ?? ''; +} + +function getManagerAccountID(policy: OnyxEntry, ownerLogin: string | undefined) { + return getAccountIDsByLogins([getManagerAccountEmail(policy, ownerLogin)]).at(0) ?? -1; } /** * Returns the accountID to whom the given expenseReport submits reports to in the given Policy. */ -function getSubmitToAccountID(policy: OnyxEntry, expenseReport: OnyxEntry): number { +function getSubmitToAccountID(policy: OnyxEntry, expenseReport: OnyxEntry, ownerLogin: string | undefined): number { const approvalRules = policy?.rules?.approvalRules; if (!isSubmitAndClose(policy) && approvalRules?.length) { @@ -1567,7 +1569,7 @@ function getSubmitToAccountID(policy: OnyxEntry, expenseReport: OnyxEntr } } - return getManagerAccountID(policy, expenseReport); + return getManagerAccountID(policy, ownerLogin); } function getSubmitReportManagerAccountID(policy: OnyxEntry, expenseReport: OnyxEntry, submitterLogin: string | undefined): number | undefined { @@ -1575,7 +1577,7 @@ function getSubmitReportManagerAccountID(policy: OnyxEntry, expenseRepor const existingManagerID = expenseReport?.managerID; const approvalRules = policy?.rules?.approvalRules; const ruleApprover = !isSubmitAndClose(policy) && approvalRules?.length ? getFirstRuleApprover(approvalRules, expenseReport) : ''; - const submitToAccountID = ruleApprover ? (getAccountIDsByLogins([ruleApprover]).at(0) ?? -1) : getManagerAccountID(policy, expenseReport); + const submitToAccountID = getSubmitToAccountID(policy, expenseReport, submitterLogin); const isValidSubmitToAccountID = isValidAccountRoute(submitToAccountID); const isValidExistingManagerID = isValidAccountRoute(existingManagerID ?? CONST.DEFAULT_NUMBER_ID) && existingManagerID !== ownerAccountID; const hasReliablePolicyRoute = @@ -1594,11 +1596,6 @@ function getSubmitReportManagerAccountID(policy: OnyxEntry, expenseRepor return isValidSubmitToAccountID ? submitToAccountID : existingManagerID; } -function getManagerAccountEmail(policy: OnyxEntry, expenseReport: OnyxEntry): string { - const managerAccountID = getManagerAccountID(policy, expenseReport); - return getLoginsByAccountIDs([managerAccountID]).at(0) ?? ''; -} - /** * Returns the email of the account to forward the report to depending on the approver's approval limit. * Used for advanced approval mode only. diff --git a/src/libs/ReportPreviewActionUtils.ts b/src/libs/ReportPreviewActionUtils.ts index c11a5cbe65ac..e0187355c142 100644 --- a/src/libs/ReportPreviewActionUtils.ts +++ b/src/libs/ReportPreviewActionUtils.ts @@ -37,6 +37,7 @@ function canSubmit( isReportArchived: boolean, currentUserAccountID: number, currentUserEmail: string, + ownerLogin: string | undefined, violations?: OnyxCollection, policy?: Policy, transactions?: Transaction[], @@ -59,7 +60,7 @@ function canSubmit( return false; } - const submitToAccountID = getSubmitToAccountID(policy, report); + const submitToAccountID = getSubmitToAccountID(policy, report, ownerLogin); if (submitToAccountID === report.ownerAccountID && policy?.preventSelfApproval) { return false; @@ -213,6 +214,7 @@ function getReportPreviewAction({ isDEWSubmitPending, violationsData, reportMetadata, + ownerLogin, }: { isReportArchived: boolean; currentUserAccountID: number; @@ -228,6 +230,7 @@ function getReportPreviewAction({ isDEWSubmitPending?: boolean; violationsData?: OnyxCollection; reportMetadata: OnyxEntry; + ownerLogin: string | undefined; }): ValueOf { if (!report) { return CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW; @@ -250,7 +253,7 @@ function getReportPreviewAction({ return CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW; } - if (canSubmit(report, isReportArchived, currentUserAccountID, currentUserLogin, violationsData, policy, transactions)) { + if (canSubmit(report, isReportArchived, currentUserAccountID, currentUserLogin, ownerLogin, violationsData, policy, transactions)) { return CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT; } if (canApprove(report, currentUserAccountID, reportMetadata, policy, transactions)) { diff --git a/src/libs/ReportPrimaryActionUtils.ts b/src/libs/ReportPrimaryActionUtils.ts index 3eaa2b7dcdbf..27c65eb8c71f 100644 --- a/src/libs/ReportPrimaryActionUtils.ts +++ b/src/libs/ReportPrimaryActionUtils.ts @@ -77,6 +77,7 @@ type GetReportPrimaryActionParams = { reportMetadata?: OnyxEntry; isChatReportArchived: boolean; invoiceReceiverPolicy?: Policy; + ownerLogin: string | undefined; }; type IsPrimaryPayActionParams = { @@ -108,6 +109,7 @@ function isSubmitAction( report: Report, reportTransactions: Transaction[], reportMetadata: OnyxEntry, + ownerLogin: string | undefined, policy?: Policy, reportNameValuePairs?: ReportNameValuePairs, violations?: OnyxCollection, @@ -146,7 +148,7 @@ function isSubmitAction( } } - const submitToAccountID = getSubmitToAccountID(policy, report); + const submitToAccountID = getSubmitToAccountID(policy, report, ownerLogin); if (submitToAccountID === report.ownerAccountID && policy?.preventSelfApproval && !isReportSubmitter) { return false; @@ -469,6 +471,7 @@ function getReportPrimaryAction(params: GetReportPrimaryActionParams): ValueOf; currentUserLogin?: string; currentUserAccountID: number; + ownerLogin: string | undefined; }): boolean { if (isArchivedReport(reportNameValuePairs) || isChatReportArchived) { return false; @@ -258,7 +260,7 @@ function isSubmitAction({ return false; } - const submitToAccountID = getSubmitToAccountID(policy, report); + const submitToAccountID = getSubmitToAccountID(policy, report, ownerLogin); if (submitToAccountID === report.ownerAccountID && policy?.preventSelfApproval) { return false; } @@ -978,6 +980,7 @@ function getSecondaryReportActions({ reportActions, reportMetadata, isChatReportArchived, + ownerLogin: submitterLogin, }); if ( @@ -993,6 +996,7 @@ function getSecondaryReportActions({ violations, currentUserLogin, currentUserAccountID, + ownerLogin: submitterLogin, }) ) { options.push(CONST.REPORT.SECONDARY_ACTIONS.SUBMIT); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3738743afe70..153dd2630042 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -133,7 +133,7 @@ import Parser from './Parser'; import {getParsedMessageWithShortMentions} from './ParsingUtils'; import {getBankAccountLastFourDigits} from './PaymentUtils'; import Permissions from './Permissions'; -import {getAccountIDsByLogins, getDisplayNameOrDefault, getLoginsByAccountIDs, getPersonalDetailByEmail, getShortMentionIfFound} from './PersonalDetailsUtils'; +import {getAccountIDsByLogins, getDisplayNameOrDefault, getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail, getShortMentionIfFound} from './PersonalDetailsUtils'; import { canSendInvoiceFromWorkspace, getActivePolicies, @@ -2032,7 +2032,7 @@ function isAwaitingFirstLevelApproval(report: OnyxEntry): boolean { } // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - const submitsToAccountID = getSubmitToAccountID(getPolicy(report.policyID), report); + const submitsToAccountID = getSubmitToAccountID(getPolicy(report.policyID), report, getLoginByAccountID(report.ownerAccountID, allPersonalDetails)); return isProcessingReport(report) && submitsToAccountID === report.managerID && !hasReportBeenForwardedSinceLastSubmit(report); } @@ -4919,7 +4919,9 @@ function canEditMoneyRequest( } if (reportPolicy?.type === CONST.POLICY.TYPE.CORPORATE && moneyRequestReport && isSubmitted && isCurrentUserSubmitter(moneyRequestReport)) { - const isForwarded = getSubmitToAccountID(reportPolicy, moneyRequestReport) !== moneyRequestReport.managerID || hasReportBeenForwardedSinceLastSubmit(moneyRequestReport); + const isForwarded = + getSubmitToAccountID(reportPolicy, moneyRequestReport, getLoginByAccountID(moneyRequestReport.ownerAccountID, allPersonalDetails)) !== moneyRequestReport.managerID || + hasReportBeenForwardedSinceLastSubmit(moneyRequestReport); return !isForwarded; } @@ -4938,7 +4940,7 @@ function getNextApproverAccountID(report: OnyxEntry, isUnapproved = fals } const approvalChain = getApprovalChain(policy, report); - const submitToAccountID = getSubmitToAccountID(policy, report); + const submitToAccountID = getSubmitToAccountID(policy, report, getLoginByAccountID(report?.ownerAccountID, allPersonalDetails)); if (isUnapproved) { if (approvalChain.includes(deprecatedCurrentUserEmail ?? '')) { @@ -5971,7 +5973,7 @@ function getChatRoomSubtitle(report: OnyxEntry, policy: OnyxEntry, parentReportActionID: string, policy: OnyxEntry, @@ -6796,7 +6799,7 @@ function buildOptimisticEmptyReport( parentReportID: parentReport?.reportID, parentReportActionID, chatReportID: parentReport?.reportID, - managerID: getManagerAccountID(policy, {ownerAccountID: accountID}), + managerID: getManagerAccountID(policy, login), }; // Compute optimistic report name if applicable @@ -12524,7 +12527,7 @@ function getApprovalChain(policy: OnyxEntry, expenseReport: OnyxEntry 0 && allReportTransactions.every((t) => isScanning(t) || isPending(t)); - const submitToAccountID = getSubmitToAccountID(policy, report); + const submitToAccountID = getSubmitToAccountID(policy, report, getLoginByAccountID(report.ownerAccountID, data.personalDetailsList)); const isAllowedToApproveExpenseReport = isAllowedToApproveExpenseReportUtils(report, submitToAccountID, policy); // We're not supporting approve partial amount on search page now diff --git a/src/libs/actions/OnyxDerived/configs/todos.ts b/src/libs/actions/OnyxDerived/configs/todos.ts index edc9f0a61d9a..c1bc8103e4c5 100644 --- a/src/libs/actions/OnyxDerived/configs/todos.ts +++ b/src/libs/actions/OnyxDerived/configs/todos.ts @@ -1,10 +1,11 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import {getLoginByAccountID} from '@libs/PersonalDetailsUtils'; import {isApproveAction, isExportAction, isPrimaryPayAction, isSubmitAction} from '@libs/ReportPrimaryActionUtils'; import {hasOnlyHeldExpenses, hasOnlyNonReimbursableTransactions} from '@libs/ReportUtils'; import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {BankAccountList, Policy, Report, ReportActions, ReportMetadata, ReportNameValuePairs, Transaction} from '@src/types/onyx'; +import type {BankAccountList, PersonalDetailsList, Policy, Report, ReportActions, ReportMetadata, ReportNameValuePairs, Transaction} from '@src/types/onyx'; type CreateTodosReportsAndTransactionsParams = { allReports: OnyxCollection; @@ -13,6 +14,7 @@ type CreateTodosReportsAndTransactionsParams = { allReportNameValuePairs: OnyxCollection; allReportActions: OnyxCollection; allReportMetadata: OnyxCollection; + personalDetailsList: OnyxEntry; bankAccountList: OnyxEntry; currentUserAccountID: number; login: string; @@ -25,6 +27,7 @@ const createTodosReportsAndTransactions = ({ allReportNameValuePairs, allReportActions, allReportMetadata, + personalDetailsList, bankAccountList, currentUserAccountID, login, @@ -59,7 +62,20 @@ const createTodosReportsAndTransactions = ({ const reportTransactions = transactionsByReportID[report.reportID] ?? []; const reportMetadata = allReportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report.reportID}`]; const allExpensesHeld = hasOnlyHeldExpenses(reportTransactions); - if (isSubmitAction(report, reportTransactions, reportMetadata, policy, reportNameValuePair, undefined, login, currentUserAccountID) && !allExpensesHeld) { + if ( + isSubmitAction( + report, + reportTransactions, + reportMetadata, + getLoginByAccountID(report.ownerAccountID, personalDetailsList), + policy, + reportNameValuePair, + undefined, + login, + currentUserAccountID, + ) && + !allExpensesHeld + ) { reportsToSubmit.push(report); } if (isApproveAction(report, reportTransactions, currentUserAccountID, reportMetadata, policy) && !allExpensesHeld) { @@ -112,6 +128,7 @@ export default createOnyxDerivedValueConfig({ allReportNameValuePairs, allReportActions, allReportMetadata, + personalDetailsList, bankAccountList, currentUserAccountID: userAccountID, login, diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 593da3386411..277c7e5ce358 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -3980,7 +3980,7 @@ function buildNewReportOptimisticData( const {accountID, login, email} = ownerPersonalDetails; const timeOfCreation = DateUtils.getDBTime(); const parentReport = getPolicyExpenseChat(accountID, policy?.id); - const optimisticReportData = buildOptimisticEmptyReport(reportID, accountID, parentReport, reportPreviewReportActionID, policy, timeOfCreation, betas); + const optimisticReportData = buildOptimisticEmptyReport(reportID, accountID, login, parentReport, reportPreviewReportActionID, policy, timeOfCreation, betas); if (reportName) { optimisticReportData.reportName = reportName; @@ -7068,6 +7068,7 @@ function buildOptimisticChangePolicyData({ policy, currentUserAccountID, currentUserEmail, + ownerLogin, managerLogin, hasViolationsParam, isASAPSubmitBetaEnabled, @@ -7081,6 +7082,7 @@ function buildOptimisticChangePolicyData({ policy: Policy; currentUserAccountID: number; currentUserEmail: string; + ownerLogin: string | undefined; managerLogin: string | undefined; hasViolationsParam: boolean; isASAPSubmitBetaEnabled: boolean; @@ -7117,7 +7119,7 @@ function buildOptimisticChangePolicyData({ const reportIDToThreadsReportIDsMap = buildReportIDToThreadsReportIDsMap(); updatePolicyIdForReportAndThreads(reportID, policy.id, reportIDToThreadsReportIDsMap, optimisticData, failureData); - const newManagerAccountID = getSubmitToAccountID(policy, report); + const newManagerAccountID = getSubmitToAccountID(policy, report, ownerLogin); const shouldResetApprovalChain = isProcessingReport(report) && newManagerAccountID !== report.managerID && managerLogin && isPolicyMember(policy, managerLogin); if (shouldResetApprovalChain) { optimisticData.push({ @@ -7567,6 +7569,7 @@ function changeReportPolicy({ policy, currentUserAccountID, email, + ownerLogin, managerLogin, hasViolationsParam, isChangePolicyTrainingModalDismissed, @@ -7580,6 +7583,7 @@ function changeReportPolicy({ policy: Policy; currentUserAccountID: number; email: string; + ownerLogin: string | undefined; managerLogin: string | undefined; hasViolationsParam: boolean; isChangePolicyTrainingModalDismissed: boolean; @@ -7598,6 +7602,7 @@ function changeReportPolicy({ policy, currentUserAccountID, currentUserEmail: email, + ownerLogin, managerLogin, hasViolationsParam, isASAPSubmitBetaEnabled, @@ -7699,6 +7704,7 @@ function changeReportPolicyAndInviteSubmitter({ policy, currentUserAccountID, currentUserEmail, + ownerLogin: submitterLogin, managerLogin, hasViolationsParam, isASAPSubmitBetaEnabled, diff --git a/src/pages/DynamicReportChangeWorkspacePage.tsx b/src/pages/DynamicReportChangeWorkspacePage.tsx index 9c086511713f..9185d99bd739 100644 --- a/src/pages/DynamicReportChangeWorkspacePage.tsx +++ b/src/pages/DynamicReportChangeWorkspacePage.tsx @@ -147,6 +147,7 @@ function DynamicReportChangeWorkspacePage({report}: DynamicReportChangeWorkspace policy, currentUserAccountID: session?.accountID ?? CONST.DEFAULT_NUMBER_ID, email: session?.email ?? '', + ownerLogin: submitterLogin, managerLogin, hasViolationsParam: hasViolations, isChangePolicyTrainingModalDismissed, diff --git a/tests/actions/IOUTest/ReportWorkflowTest.ts b/tests/actions/IOUTest/ReportWorkflowTest.ts index 309735dc6f5b..b53e4e2e9d23 100644 --- a/tests/actions/IOUTest/ReportWorkflowTest.ts +++ b/tests/actions/IOUTest/ReportWorkflowTest.ts @@ -3331,6 +3331,7 @@ describe('actions/IOU/ReportWorkflow', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: employeeEmail, }); expect(previewAction).not.toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.APPROVE); }); diff --git a/tests/actions/ReportPreviewActionUtilsTest.ts b/tests/actions/ReportPreviewActionUtilsTest.ts index ab9c857af91b..6d8460827151 100644 --- a/tests/actions/ReportPreviewActionUtilsTest.ts +++ b/tests/actions/ReportPreviewActionUtilsTest.ts @@ -84,6 +84,7 @@ describe('getReportPreviewAction', () => { transactions: [], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.ADD_EXPENSE); }); @@ -127,6 +128,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT); }); @@ -172,6 +174,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).not.toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT); }); @@ -218,6 +221,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).not.toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT); }); @@ -262,6 +266,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT); }); @@ -306,6 +311,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT); }); @@ -365,6 +371,7 @@ describe('getReportPreviewAction', () => { isDEWSubmitPending: undefined, violationsData: violations, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); @@ -410,6 +417,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); @@ -452,6 +460,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.APPROVE); }); @@ -491,6 +500,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); @@ -531,6 +541,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); @@ -568,6 +579,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); @@ -606,6 +618,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.APPROVE); }); @@ -648,6 +661,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.APPROVE); }); @@ -684,6 +698,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY); }); @@ -721,6 +736,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); @@ -761,6 +777,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY); }); @@ -802,6 +819,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); @@ -843,6 +861,7 @@ describe('getReportPreviewAction', () => { invoiceReceiverPolicy, bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY); }); @@ -900,6 +919,7 @@ describe('getReportPreviewAction', () => { bankAccountList: {}, invoiceReceiverPolicy, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); @@ -945,6 +965,7 @@ describe('getReportPreviewAction', () => { bankAccountList: {}, invoiceReceiverPolicy, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY); }); @@ -985,6 +1006,7 @@ describe('getReportPreviewAction', () => { bankAccountList: {}, invoiceReceiverPolicy: undefined, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); @@ -1019,6 +1041,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }), ).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.EXPORT_TO_ACCOUNTING); }); @@ -1060,6 +1083,7 @@ describe('getReportPreviewAction', () => { isSubmittingAnimationRunning: false, isDEWSubmitPending: true, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }); // Then it should return VIEW because DEW submission is pending offline @@ -1106,6 +1130,7 @@ describe('getReportPreviewAction', () => { isSubmittingAnimationRunning: false, isDEWSubmitPending: false, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }); // Then it should allow SUBMIT because failed submissions can be retried (not VIEW) @@ -1152,6 +1177,7 @@ describe('getReportPreviewAction', () => { isSubmittingAnimationRunning: false, isDEWSubmitPending: false, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }); // Then it should not return VIEW because DEW submit did not fail and regular logic applies @@ -1200,6 +1226,7 @@ describe('getReportPreviewAction', () => { reportMetadata: { pendingExpenseAction: CONST.EXPENSE_PENDING_ACTION.APPROVE, }, + ownerLogin: CURRENT_USER_EMAIL, }); // Then it should return VIEW because DEW approval is pending offline @@ -1244,6 +1271,7 @@ describe('getReportPreviewAction', () => { transactions: [transaction], bankAccountList: {}, reportMetadata: undefined, + ownerLogin: CURRENT_USER_EMAIL, }); // Then it should return APPROVE because DEW approval is not pending diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 16e7cfd9c40b..cc41042ae287 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -3206,6 +3206,7 @@ describe('actions/Report', () => { managerLogin: '', hasViolationsParam: true, isChangePolicyTrainingModalDismissed: false, + ownerLogin: undefined, isASAPSubmitBetaEnabled: false, reportPreviewAction: undefined, }); @@ -3264,6 +3265,7 @@ describe('actions/Report', () => { managerLogin: '', hasViolationsParam: false, isChangePolicyTrainingModalDismissed: false, + ownerLogin: undefined, isASAPSubmitBetaEnabled: false, reportPreviewAction: undefined, }); @@ -3332,6 +3334,7 @@ describe('actions/Report', () => { managerLogin: '', hasViolationsParam: false, isChangePolicyTrainingModalDismissed: false, + ownerLogin: undefined, isASAPSubmitBetaEnabled: false, reportPreviewAction: undefined, }); @@ -3426,6 +3429,7 @@ describe('actions/Report', () => { managerLogin: '', hasViolationsParam: false, isChangePolicyTrainingModalDismissed: false, + ownerLogin: undefined, isASAPSubmitBetaEnabled: false, reportPreviewAction: undefined, }); @@ -3508,6 +3512,7 @@ describe('actions/Report', () => { managerLogin: '', hasViolationsParam: false, isChangePolicyTrainingModalDismissed: false, + ownerLogin: undefined, isASAPSubmitBetaEnabled: false, reportPreviewAction: undefined, }); @@ -4330,6 +4335,7 @@ describe('actions/Report', () => { policy, currentUserAccountID: 1, currentUserEmail: '', + ownerLogin: undefined, managerLogin: '', hasViolationsParam: false, isASAPSubmitBetaEnabled: true, @@ -4381,6 +4387,7 @@ describe('actions/Report', () => { policy, currentUserAccountID: 1, currentUserEmail: '', + ownerLogin: undefined, managerLogin: '', hasViolationsParam: false, isASAPSubmitBetaEnabled: true, @@ -4440,6 +4447,7 @@ describe('actions/Report', () => { policy, currentUserAccountID: 1, currentUserEmail: '', + ownerLogin: undefined, managerLogin: '', hasViolationsParam: false, isASAPSubmitBetaEnabled: true, @@ -4486,6 +4494,7 @@ describe('actions/Report', () => { policy, currentUserAccountID: 1, currentUserEmail: '', + ownerLogin: undefined, managerLogin: '', hasViolationsParam: false, isASAPSubmitBetaEnabled: true, @@ -4545,6 +4554,7 @@ describe('actions/Report', () => { policy, currentUserAccountID: 1, currentUserEmail: '', + ownerLogin: undefined, managerLogin: '', hasViolationsParam: false, isASAPSubmitBetaEnabled: true, @@ -4606,6 +4616,7 @@ describe('actions/Report', () => { policy, currentUserAccountID: 1, currentUserEmail: '', + ownerLogin: undefined, managerLogin: '', hasViolationsParam: false, isASAPSubmitBetaEnabled: true, diff --git a/tests/perf-test/PolicyUtils.perf-test.ts b/tests/perf-test/PolicyUtils.perf-test.ts index 370b38fbfcb0..0ab2b723c111 100644 --- a/tests/perf-test/PolicyUtils.perf-test.ts +++ b/tests/perf-test/PolicyUtils.perf-test.ts @@ -66,7 +66,7 @@ describe('PolicyUtils', () => { 100000, ); await Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, transactions); - await measureFunction(() => getSubmitToAccountID(policy, expenseReport)); + await measureFunction(() => getSubmitToAccountID(policy, expenseReport, '')); }); describe('not a submit and close policy', () => { @@ -98,7 +98,7 @@ describe('PolicyUtils', () => { 10000, ); await Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, transactions); - await measureFunction(() => getSubmitToAccountID(policy, expenseReport)); + await measureFunction(() => getSubmitToAccountID(policy, expenseReport, '')); }); test('all transactions have category, but no category approval rules', async () => { @@ -128,7 +128,7 @@ describe('PolicyUtils', () => { 10000, ); await Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, transactions); - await measureFunction(() => getSubmitToAccountID(policy, expenseReport)); + await measureFunction(() => getSubmitToAccountID(policy, expenseReport, '')); }); }); }); diff --git a/tests/unit/NextStepUtilsTest.ts b/tests/unit/NextStepUtilsTest.ts index 884c1e5a5979..3a74bfdbde59 100644 --- a/tests/unit/NextStepUtilsTest.ts +++ b/tests/unit/NextStepUtilsTest.ts @@ -95,6 +95,7 @@ describe('libs/NextStepUtils', () => { const emptyReport = buildOptimisticEmptyReport( 'fake-empty-report-id-2', currentUserAccountID, + currentUserEmail, {reportID: 'fake-parent-report-id-3'}, 'fake-parent-report-action-id-4', policy, diff --git a/tests/unit/PolicyUtilsTest.ts b/tests/unit/PolicyUtilsTest.ts index f2959432ff58..0023fc540d62 100644 --- a/tests/unit/PolicyUtilsTest.ts +++ b/tests/unit/PolicyUtilsTest.ts @@ -715,7 +715,7 @@ describe('PolicyUtils', () => { ownerAccountID: employeeAccountID, type: CONST.REPORT.TYPE.EXPENSE, }; - expect(getSubmitToAccountID(policy, expenseReport)).toBe(ownerAccountID); + expect(getSubmitToAccountID(policy, expenseReport, employeeEmail)).toBe(ownerAccountID); }); it('should return the policy approver/owner if the policy use the optional workflow', () => { const policy: Policy = { @@ -730,7 +730,7 @@ describe('PolicyUtils', () => { ownerAccountID: employeeAccountID, type: CONST.REPORT.TYPE.EXPENSE, }; - expect(getSubmitToAccountID(policy, expenseReport)).toBe(ownerAccountID); + expect(getSubmitToAccountID(policy, expenseReport, employeeEmail)).toBe(ownerAccountID); }); it('should return the employee submitsTo if the policy use the advance workflow', () => { const policy: Policy = { @@ -746,7 +746,7 @@ describe('PolicyUtils', () => { ownerAccountID: employeeAccountID, type: CONST.REPORT.TYPE.EXPENSE, }; - expect(getSubmitToAccountID(policy, expenseReport)).toBe(adminAccountID); + expect(getSubmitToAccountID(policy, expenseReport, employeeEmail)).toBe(adminAccountID); }); }); describe('Has category/tag approver', () => { @@ -779,7 +779,7 @@ describe('PolicyUtils', () => { [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction1.transactionID}`]: transaction1, [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction2.transactionID}`]: transaction2, } as unknown as OnyxMultiSetInput); - expect(getSubmitToAccountID(policy, expenseReport)).toBe(categoryApprover1AccountID); + expect(getSubmitToAccountID(policy, expenseReport, employeeEmail)).toBe(categoryApprover1AccountID); }); it('should return default approver if rule approver is submitter and prevent self approval is enabled', async () => { const policy: Policy = { @@ -805,7 +805,7 @@ describe('PolicyUtils', () => { }; await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, transaction); - expect(getSubmitToAccountID(policy, expenseReport)).toBe(adminAccountID); + expect(getSubmitToAccountID(policy, expenseReport, categoryApprover1Email)).toBe(adminAccountID); }); it('should return the category approver of the first transaction sorted by created if we have many transaction categories match with the category approver rule', async () => { const policy: Policy = { @@ -838,7 +838,7 @@ describe('PolicyUtils', () => { [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction1.transactionID}`]: transaction1, [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction2.transactionID}`]: transaction2, } as unknown as OnyxMultiSetInput); - expect(getSubmitToAccountID(policy, expenseReport)).toBe(categoryApprover2AccountID); + expect(getSubmitToAccountID(policy, expenseReport, employeeEmail)).toBe(categoryApprover2AccountID); }); it('should return the first rule approver who is not the current submitter', async () => { const policy: Policy = { @@ -885,7 +885,7 @@ describe('PolicyUtils', () => { [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction3.transactionID}`]: transaction3, } as unknown as OnyxMultiSetInput); - expect(getSubmitToAccountID(policy, expenseReport)).toBe(tagApprover1AccountID); + expect(getSubmitToAccountID(policy, expenseReport, categoryApprover1Email)).toBe(tagApprover1AccountID); }); describe('Has no transaction match with the category approver rule', () => { it('should return the first tag approver if has any transaction tag match with with the tag approver rule ', async () => { @@ -921,7 +921,7 @@ describe('PolicyUtils', () => { [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction1.transactionID}`]: transaction1, [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction2.transactionID}`]: transaction2, } as unknown as OnyxMultiSetInput); - expect(getSubmitToAccountID(policy, expenseReport)).toBe(tagApprover1AccountID); + expect(getSubmitToAccountID(policy, expenseReport, employeeEmail)).toBe(tagApprover1AccountID); }); it('should return the tag approver of the first transaction sorted by created if we have many transaction tags match with the tag approver rule', async () => { const policy: Policy = { @@ -956,7 +956,7 @@ describe('PolicyUtils', () => { [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction1.transactionID}`]: transaction1, [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction2.transactionID}`]: transaction2, } as unknown as OnyxMultiSetInput); - expect(getSubmitToAccountID(policy, expenseReport)).toBe(tagApprover2AccountID); + expect(getSubmitToAccountID(policy, expenseReport, employeeEmail)).toBe(tagApprover2AccountID); }); }); }); @@ -1011,10 +1011,7 @@ describe('PolicyUtils', () => { type: CONST.POLICY.TYPE.PERSONAL, approver: categoryApprover1Email, }; - const report: Report = { - ...createRandomReport(0, undefined), - }; - const result = getManagerAccountID(policy, report); + const result = getManagerAccountID(policy, ''); expect(result).toBe(categoryApprover1AccountID); }); @@ -1027,11 +1024,8 @@ describe('PolicyUtils', () => { approver: undefined, owner: '', }; - const report: Report = { - ...createRandomReport(0, undefined), - }; - const result = getManagerAccountID(policy, report); + const result = getManagerAccountID(policy, ''); expect(result).toBe(-1); }); @@ -1048,12 +1042,8 @@ describe('PolicyUtils', () => { }, }, }; - const report: Report = { - ...createRandomReport(0, undefined), - ownerAccountID: employeeAccountID, - }; - const result = getManagerAccountID(policy, report); + const result = getManagerAccountID(policy, employeeEmail); expect(result).toBe(adminAccountID); }); @@ -1065,12 +1055,8 @@ describe('PolicyUtils', () => { approvalMode: undefined, approver: categoryApprover1Email, }; - const report: Report = { - ...createRandomReport(0, undefined), - ownerAccountID: employeeAccountID, - }; - const result = getManagerAccountID(policy, report); + const result = getManagerAccountID(policy, ''); expect(result).toBe(categoryApprover1AccountID); }); diff --git a/tests/unit/ReportPrimaryActionUtilsTest.ts b/tests/unit/ReportPrimaryActionUtilsTest.ts index 5d220df5bb80..e37a76111459 100644 --- a/tests/unit/ReportPrimaryActionUtilsTest.ts +++ b/tests/unit/ReportPrimaryActionUtilsTest.ts @@ -2,6 +2,7 @@ import {renderHook} from '@testing-library/react-native'; import Onyx from 'react-native-onyx'; import type {OnyxCollection} from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; +import {getLoginByAccountID} from '@libs/PersonalDetailsUtils'; import {getValidConnectedIntegration, isPreferredExporter} from '@libs/PolicyUtils'; import type * as PolicyUtils from '@libs/PolicyUtils'; import { @@ -77,6 +78,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [], violations: {}, @@ -108,6 +110,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -142,6 +145,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -176,6 +180,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -211,6 +216,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -245,6 +251,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -280,6 +287,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -313,6 +321,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -352,6 +361,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -389,6 +399,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -534,6 +545,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -577,6 +589,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -609,6 +622,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -643,6 +657,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -676,6 +691,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -716,6 +732,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -757,6 +774,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -795,6 +813,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -833,6 +852,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [], violations: {}, @@ -899,6 +919,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -958,6 +979,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -1031,6 +1053,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -1092,6 +1115,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -1133,6 +1157,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${TRANSACTION_ID}`]: [violation]}, @@ -1171,6 +1196,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${TRANSACTION_ID}`]: [violation]}, @@ -1209,6 +1235,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${TRANSACTION_ID}`]: [violation]}, @@ -1247,6 +1274,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${TRANSACTION_ID}`]: [violation]}, @@ -1286,6 +1314,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${TRANSACTION_ID}`]: [violation]}, @@ -1322,6 +1351,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -1361,6 +1391,7 @@ describe('getPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport: invoiceChatReport, reportTransactions: [transaction], violations: {}, @@ -1621,6 +1652,7 @@ describe('getTransactionThreadPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -1661,6 +1693,7 @@ describe('getTransactionThreadPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, @@ -1702,6 +1735,7 @@ describe('getTransactionThreadPrimaryAction', () => { currentUserLogin: CURRENT_USER_EMAIL, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, report, + ownerLogin: '', chatReport, reportTransactions: [transaction], violations: {}, diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 01d5453e88ed..6f956a63c9cc 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -16488,7 +16488,7 @@ describe('ReportUtils', () => { const timeOfCreation = DateUtils.getDBTime(); // Then the report name should be "New Report" - const optimisticReport = buildOptimisticEmptyReport(reportID, accountID, parentReport, parentReportActionID, policyWithEmptyFieldList, timeOfCreation, betas); + const optimisticReport = buildOptimisticEmptyReport(reportID, accountID, currentUserEmail, parentReport, parentReportActionID, policyWithEmptyFieldList, timeOfCreation, betas); expect(optimisticReport.reportName).toBe(CONST.REPORT.DEFAULT_EXPENSE_REPORT_NAME); }); @@ -16517,7 +16517,7 @@ describe('ReportUtils', () => { const timeOfCreation = DateUtils.getDBTime(); // Then the report name should be "New Report" - const optimisticReport = buildOptimisticEmptyReport(reportID, accountID, parentReport, parentReportActionID, policyWithEmptyFieldList, timeOfCreation, betas); + const optimisticReport = buildOptimisticEmptyReport(reportID, accountID, currentUserEmail, parentReport, parentReportActionID, policyWithEmptyFieldList, timeOfCreation, betas); expect(optimisticReport.reportName).toBe(CONST.REPORT.DEFAULT_EXPENSE_REPORT_NAME); }); }); From b1fea896a377de15dc61f55d8f4390809d09eefc Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 27 Jun 2026 12:30:52 +0800 Subject: [PATCH 2/5] fallback to -1 when email is empty --- src/libs/PolicyUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index d4259c348fff..d8b0bb1f45a7 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1553,7 +1553,8 @@ function getManagerAccountEmail(policy: OnyxEntry, ownerLogin: string | } function getManagerAccountID(policy: OnyxEntry, ownerLogin: string | undefined) { - return getAccountIDsByLogins([getManagerAccountEmail(policy, ownerLogin)]).at(0) ?? -1; + const managerEmail = getManagerAccountEmail(policy, ownerLogin); + return managerEmail ? getAccountIDsByLogins([managerEmail]).at(0) ?? -1 : -1; } /** From cf6e82cfcffb348441bb749f789f58b68e9434c8 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 27 Jun 2026 12:31:21 +0800 Subject: [PATCH 3/5] remove unused import --- src/libs/PolicyUtils.ts | 2 +- tests/unit/ReportPrimaryActionUtilsTest.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index d8b0bb1f45a7..2fe13d874616 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -52,7 +52,7 @@ import Navigation from './Navigation/Navigation'; import {getIsOffline} from './NetworkState'; import {formatMemberForList} from './OptionsListUtils'; import type {MemberForList} from './OptionsListUtils'; -import {getAccountIDsByLogins, getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils'; +import {getAccountIDsByLogins, getLoginByAccountID, getPersonalDetailByEmail} from './PersonalDetailsUtils'; import {getAllSortedTransactions, getCategory, getTag, getTagArrayFromName} from './TransactionUtils'; import {isPublicDomain, isValidAccountRoute} from './ValidationUtils'; diff --git a/tests/unit/ReportPrimaryActionUtilsTest.ts b/tests/unit/ReportPrimaryActionUtilsTest.ts index e37a76111459..a828fdebd84d 100644 --- a/tests/unit/ReportPrimaryActionUtilsTest.ts +++ b/tests/unit/ReportPrimaryActionUtilsTest.ts @@ -2,7 +2,6 @@ import {renderHook} from '@testing-library/react-native'; import Onyx from 'react-native-onyx'; import type {OnyxCollection} from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; -import {getLoginByAccountID} from '@libs/PersonalDetailsUtils'; import {getValidConnectedIntegration, isPreferredExporter} from '@libs/PolicyUtils'; import type * as PolicyUtils from '@libs/PolicyUtils'; import { From d32fe8a2042b60f64f5dbbcaa7619ef427f6a4dc Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 27 Jun 2026 12:38:57 +0800 Subject: [PATCH 4/5] pass owner login to getFirstRuleApprover --- src/libs/PolicyUtils.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 2fe13d874616..d6fc5dc55c93 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1473,7 +1473,7 @@ function getRuleApprovers(policy: OnyxEntry, expenseReport: OnyxEntry) { +function getFirstRuleApprover(approvalRules: ApprovalRule[], expenseReport: OnyxEntry, ownerLogin: string | undefined) { // Pre-build a lookup map of { category: { value → approver }, tag: { value → approver } } // from the policy's approval rules so that each transaction's category/tag can be resolved in O(1). const rulesMap: Record<'category' | 'tag', Record> = {category: {}, tag: {}}; @@ -1504,9 +1504,6 @@ function getFirstRuleApprover(approvalRules: ApprovalRule[], expenseReport: Onyx return ''; } - const employeeAccountID = expenseReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID; - const employeeLogin = getLoginByAccountID(employeeAccountID); - let firstCategoryApprover = ''; let firstTagApprover = ''; @@ -1517,7 +1514,7 @@ function getFirstRuleApprover(approvalRules: ApprovalRule[], expenseReport: Onyx // Category approvers take strict priority over tag approvers. // Break immediately on the first match so we don't keep scanning transactions unnecessarily. - if (categoryApprover && categoryApprover !== employeeLogin) { + if (categoryApprover && categoryApprover !== ownerLogin) { firstCategoryApprover = categoryApprover; break; } @@ -1527,7 +1524,7 @@ function getFirstRuleApprover(approvalRules: ApprovalRule[], expenseReport: Onyx const tag = getTag(transaction); const tagApprover = rulesMap.tag[tag]; - if (tagApprover && tagApprover !== employeeLogin) { + if (tagApprover && tagApprover !== ownerLogin) { firstTagApprover = tagApprover; } } @@ -1564,7 +1561,7 @@ function getSubmitToAccountID(policy: OnyxEntry, expenseReport: OnyxEntr const approvalRules = policy?.rules?.approvalRules; if (!isSubmitAndClose(policy) && approvalRules?.length) { - const ruleApprover = getFirstRuleApprover(approvalRules, expenseReport); + const ruleApprover = getFirstRuleApprover(approvalRules, expenseReport, ownerLogin); if (ruleApprover) { return getAccountIDsByLogins([ruleApprover]).at(0) ?? -1; } @@ -1577,7 +1574,7 @@ function getSubmitReportManagerAccountID(policy: OnyxEntry, expenseRepor const ownerAccountID = expenseReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID; const existingManagerID = expenseReport?.managerID; const approvalRules = policy?.rules?.approvalRules; - const ruleApprover = !isSubmitAndClose(policy) && approvalRules?.length ? getFirstRuleApprover(approvalRules, expenseReport) : ''; + const ruleApprover = !isSubmitAndClose(policy) && approvalRules?.length ? getFirstRuleApprover(approvalRules, expenseReport, ownerLogin) : ''; const submitToAccountID = getSubmitToAccountID(policy, expenseReport, submitterLogin); const isValidSubmitToAccountID = isValidAccountRoute(submitToAccountID); const isValidExistingManagerID = isValidAccountRoute(existingManagerID ?? CONST.DEFAULT_NUMBER_ID) && existingManagerID !== ownerAccountID; From c6210674782b1c60e12b892664f26922b8869c07 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 27 Jun 2026 13:19:24 +0800 Subject: [PATCH 5/5] lint --- src/libs/PolicyUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index d6fc5dc55c93..bb3fc9717c5f 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -52,7 +52,7 @@ import Navigation from './Navigation/Navigation'; import {getIsOffline} from './NetworkState'; import {formatMemberForList} from './OptionsListUtils'; import type {MemberForList} from './OptionsListUtils'; -import {getAccountIDsByLogins, getLoginByAccountID, getPersonalDetailByEmail} from './PersonalDetailsUtils'; +import {getAccountIDsByLogins, getPersonalDetailByEmail} from './PersonalDetailsUtils'; import {getAllSortedTransactions, getCategory, getTag, getTagArrayFromName} from './TransactionUtils'; import {isPublicDomain, isValidAccountRoute} from './ValidationUtils'; @@ -1551,7 +1551,7 @@ function getManagerAccountEmail(policy: OnyxEntry, ownerLogin: string | function getManagerAccountID(policy: OnyxEntry, ownerLogin: string | undefined) { const managerEmail = getManagerAccountEmail(policy, ownerLogin); - return managerEmail ? getAccountIDsByLogins([managerEmail]).at(0) ?? -1 : -1; + return managerEmail ? (getAccountIDsByLogins([managerEmail]).at(0) ?? -1) : -1; } /** @@ -1574,7 +1574,7 @@ function getSubmitReportManagerAccountID(policy: OnyxEntry, expenseRepor const ownerAccountID = expenseReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID; const existingManagerID = expenseReport?.managerID; const approvalRules = policy?.rules?.approvalRules; - const ruleApprover = !isSubmitAndClose(policy) && approvalRules?.length ? getFirstRuleApprover(approvalRules, expenseReport, ownerLogin) : ''; + const ruleApprover = !isSubmitAndClose(policy) && approvalRules?.length ? getFirstRuleApprover(approvalRules, expenseReport, submitterLogin) : ''; const submitToAccountID = getSubmitToAccountID(policy, expenseReport, submitterLogin); const isValidSubmitToAccountID = isValidAccountRoute(submitToAccountID); const isValidExistingManagerID = isValidAccountRoute(existingManagerID ?? CONST.DEFAULT_NUMBER_ID) && existingManagerID !== ownerAccountID;