From 7eefefac3894d6d7e91317556d904d1f908b27c8 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Thu, 24 Apr 2025 16:17:06 -0600 Subject: [PATCH 01/19] fix avaialble workspace logic --- src/libs/PolicyUtils.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 577b5e85d6714..5aedade7024e0 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -43,6 +43,7 @@ import {isOffline as isOfflineNetworkStore} from './Network/NetworkStore'; import {getAccountIDsByLogins, getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils'; import {getAllSortedTransactions, getCategory, getTag} from './TransactionUtils'; import {isPublicDomain} from './ValidationUtils'; +import { isIOUReport } from './ReportUtils'; type MemberEmailsToAccountIDs = Record; @@ -1123,6 +1124,12 @@ const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report return false; } + // The report payer must also be a payer in the policy + const payerLogin = report?.managerID ? getLoginByAccountID(report?.managerID) : undefined; + if (!isUserPolicyAdmin(newPolicy, payerLogin)) { + return false; + } + // Submitters: workspaces where the submitter is a member of const isCurrentUserSubmitter = report?.ownerAccountID === currentUserAccountID; if (isCurrentUserSubmitter) { From c2d86dd9ca2fa9f98fa0e0d99556d32c9ac2e812 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Thu, 24 Apr 2025 16:20:01 -0600 Subject: [PATCH 02/19] add payer condition --- src/libs/PolicyUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 5aedade7024e0..7acc43365c377 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1126,7 +1126,7 @@ const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report // The report payer must also be a payer in the policy const payerLogin = report?.managerID ? getLoginByAccountID(report?.managerID) : undefined; - if (!isUserPolicyAdmin(newPolicy, payerLogin)) { + if (!isUserPolicyAdmin(newPolicy, payerLogin) || newPolicy?.achAccount?.reimburser === payerLogin) { return false; } From 43f8cbfd125eddc6fc33ea94799ac122824eddb5 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Thu, 24 Apr 2025 16:29:40 -0600 Subject: [PATCH 03/19] add iou condition --- src/libs/ReportSecondaryActionUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index c97324bf90d01..c34ab888bc015 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -330,8 +330,9 @@ function isChangeWorkspaceAction(report: Report, reportTransactions: Transaction const isReportPayer = isPayerUtils(getSession(), report, false, policy); const isReportApproved = isReportApprovedUtils({report}); + const isIOUReport = isIOUReportUtils(report); - if (isReportPayer && (isReportApproved || isClosedReport)) { + if (isReportPayer && (isExpenseReport && (isReportApproved || isClosedReport) || (isIOUReport && isProcessingReport))) { return true; } @@ -351,7 +352,6 @@ function isChangeWorkspaceAction(report: Report, reportTransactions: Transaction return true; } - const isIOUReport = isIOUReportUtils(report); const hasOnlyPersonalWorkspace = hasNoPolicyOtherThanPersonalType(); const isReportReceiver = isReportManagerUtils(report); From b218d691280d2a713e6b91c136cece84acd10198 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Thu, 24 Apr 2025 16:41:02 -0600 Subject: [PATCH 04/19] simplify logic --- src/libs/PolicyUtils.ts | 2 +- src/libs/ReportSecondaryActionUtils.ts | 61 +------------------------- 2 files changed, 2 insertions(+), 61 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 7acc43365c377..4184c448e7c79 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1126,7 +1126,7 @@ const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report // The report payer must also be a payer in the policy const payerLogin = report?.managerID ? getLoginByAccountID(report?.managerID) : undefined; - if (!isUserPolicyAdmin(newPolicy, payerLogin) || newPolicy?.achAccount?.reimburser === payerLogin) { + if (!isUserPolicyAdmin(newPolicy, payerLogin) && newPolicy?.achAccount?.reimburser !== payerLogin) { return false; } diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index c34ab888bc015..7def0885cbbc7 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -298,68 +298,9 @@ function isHoldActionForTransation(report: Report, reportTransaction: Transactio } function isChangeWorkspaceAction(report: Report, reportTransactions: Transaction[], violations: OnyxCollection, policy?: Policy): boolean { - const isExpenseReport = isExpenseReportUtils(report); - const isReportSubmitter = isCurrentUserSubmitter(report.reportID); - const areWorkflowsEnabled = !!(policy && policy.areWorkflowsEnabled); - const isClosedReport = isClosedReportUtils(report); - const policies = getAllPolicies(); const currentUserEmail = getCurrentUserEmail(); - const policiesEligibleForChange = policies.filter((newPolicy) => isWorkspaceEligibleForReportChange(newPolicy, report, policy, currentUserEmail)); - - if (policiesEligibleForChange.length <= 1) { - return false; - } - - if (isExpenseReport && isReportSubmitter && !areWorkflowsEnabled && isClosedReport) { - return true; - } - - const isOpenReport = isOpenReportUtils(report); - const isProcessingReport = isProcessingReportUtils(report); - - if (isReportSubmitter && (isOpenReport || isProcessingReport)) { - return true; - } - - const isReportApprover = isApproverUtils(policy, getCurrentUserAccountID()); - - if (isReportApprover && isProcessingReport) { - return true; - } - - const isReportPayer = isPayerUtils(getSession(), report, false, policy); - const isReportApproved = isReportApprovedUtils({report}); - const isIOUReport = isIOUReportUtils(report); - - if (isReportPayer && (isExpenseReport && (isReportApproved || isClosedReport) || (isIOUReport && isProcessingReport))) { - return true; - } - - const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; - const isReportReimbursed = isSettled(report); - const transactionIDs = reportTransactions.map((t) => t.transactionID); - const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs, violations); - - const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationForMultipleTransactions(transactionIDs, report, policy, violations); - - const userControlsReport = isReportSubmitter || isReportApprover || isAdmin; - const hasReceiptMatchViolation = hasAllPendingRTERViolations || (userControlsReport && shouldShowBrokenConnectionViolation); - const isReportExported = isExportedUtils(getReportActions(report)); - const isReportFinished = isReportApproved || isReportReimbursed || isClosedReport; - - if (isAdmin && ((!isReportExported && isReportFinished) || hasReceiptMatchViolation)) { - return true; - } - - const hasOnlyPersonalWorkspace = hasNoPolicyOtherThanPersonalType(); - const isReportReceiver = isReportManagerUtils(report); - - if (isIOUReport && !hasOnlyPersonalWorkspace && isReportReceiver && isReportReimbursed) { - return true; - } - - return false; + return policies.filter((newPolicy) => isWorkspaceEligibleForReportChange(newPolicy, report, policy, currentUserEmail)).length > 0; } function isDeleteAction(report: Report, reportTransactions: Transaction[]): boolean { From 963458d84014de41095a8744f8cd839a7dbac7d7 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Thu, 24 Apr 2025 16:44:14 -0600 Subject: [PATCH 05/19] refactor code --- src/libs/ReportSecondaryActionUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 7def0885cbbc7..490d455750c14 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -297,7 +297,7 @@ function isHoldActionForTransation(report: Report, reportTransaction: Transactio return isProcessingReport; } -function isChangeWorkspaceAction(report: Report, reportTransactions: Transaction[], violations: OnyxCollection, policy?: Policy): boolean { +function isChangeWorkspaceAction(report: Report, policy?: Policy): boolean { const policies = getAllPolicies(); const currentUserEmail = getCurrentUserEmail(); return policies.filter((newPolicy) => isWorkspaceEligibleForReportChange(newPolicy, report, policy, currentUserEmail)).length > 0; @@ -369,7 +369,7 @@ function getSecondaryReportActions( options.push(CONST.REPORT.SECONDARY_ACTIONS.DOWNLOAD); - if (isChangeWorkspaceAction(report, reportTransactions, violations, policy)) { + if (isChangeWorkspaceAction(report, policy)) { options.push(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE); } From 8fbd98ef659df8cf614faf284fe70d0928237de7 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Thu, 24 Apr 2025 17:41:53 -0600 Subject: [PATCH 06/19] move logic --- src/libs/PolicyUtils.ts | 46 ------------------------- src/libs/ReportSecondaryActionUtils.ts | 4 +-- src/libs/ReportUtils.ts | 40 +++++++++++++++++++++ src/pages/ReportChangeWorkspacePage.tsx | 4 +-- tests/unit/PolicyUtilsTest.ts | 2 +- 5 files changed, 44 insertions(+), 52 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 4184c448e7c79..bba218f72a8cc 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -43,7 +43,6 @@ import {isOffline as isOfflineNetworkStore} from './Network/NetworkStore'; import {getAccountIDsByLogins, getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils'; import {getAllSortedTransactions, getCategory, getTag} from './TransactionUtils'; import {isPublicDomain} from './ValidationUtils'; -import { isIOUReport } from './ReportUtils'; type MemberEmailsToAccountIDs = Record; @@ -1109,50 +1108,6 @@ const sortWorkspacesBySelected = (workspace1: WorkspaceDetails, workspace2: Work return workspace1.name?.toLowerCase().localeCompare(workspace2.name?.toLowerCase() ?? '') ?? 0; }; -/** - * Determines whether the report can be moved to the workspace. - */ -const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report: OnyxEntry, oldPolicy: OnyxEntry, currentUserLogin: string | undefined): boolean => { - const currentUserAccountID = getCurrentUserAccountID(); - const isCurrentUserMember = !!currentUserLogin && !!newPolicy?.employeeList?.[currentUserLogin]; - if (!isCurrentUserMember) { - return false; - } - - const isAdmin = isUserPolicyAdmin(newPolicy, currentUserLogin); - if (report?.stateNum && report?.stateNum > CONST.REPORT.STATE_NUM.SUBMITTED && !isAdmin) { - return false; - } - - // The report payer must also be a payer in the policy - const payerLogin = report?.managerID ? getLoginByAccountID(report?.managerID) : undefined; - if (!isUserPolicyAdmin(newPolicy, payerLogin) && newPolicy?.achAccount?.reimburser !== payerLogin) { - return false; - } - - // Submitters: workspaces where the submitter is a member of - const isCurrentUserSubmitter = report?.ownerAccountID === currentUserAccountID; - if (isCurrentUserSubmitter) { - return true; - } - - // Approvers: workspaces where both the approver AND submitter are members of - const reportApproverAccountID = getSubmitToAccountID(oldPolicy, report); - const isCurrentUserApprover = currentUserAccountID === reportApproverAccountID; - if (isCurrentUserApprover) { - const reportSubmitterLogin = report?.ownerAccountID ? getLoginByAccountID(report?.ownerAccountID) : undefined; - const isReportSubmitterMember = !!reportSubmitterLogin && !!newPolicy?.employeeList?.[reportSubmitterLogin]; - return isCurrentUserApprover && isReportSubmitterMember; - } - - // Admins: same as approvers OR workspaces where the admin is an admin of (note that the submitter is invited to the workspace in this case) - if (isPolicyOwner(newPolicy, currentUserAccountID) || isAdmin) { - return true; - } - - return false; -}; - /** * Takes removes pendingFields and errorFields from a customUnit */ @@ -1562,7 +1517,6 @@ export { getPolicyNameByID, getMostFrequentEmailDomain, getDescriptionForPolicyDomainCard, - isWorkspaceEligibleForReportChange, getManagerAccountID, isPrefferedExporter, areAllGroupPoliciesExpenseChatDisabled, diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 490d455750c14..8886f62f66cac 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -12,9 +12,7 @@ import { getSubmitToAccountID, hasAccountingConnections, hasIntegrationAutoSync, - hasNoPolicyOtherThanPersonalType, isPrefferedExporter, - isWorkspaceEligibleForReportChange, } from './PolicyUtils'; import {getIOUActionForReportID, getReportActions, isPayAction} from './ReportActionsUtils'; import { @@ -28,8 +26,8 @@ import { isPayer as isPayerUtils, isProcessingReport as isProcessingReportUtils, isReportApproved as isReportApprovedUtils, - isReportManager as isReportManagerUtils, isSettled, + isWorkspaceEligibleForReportChange, } from './ReportUtils'; import {getSession} from './SessionUtils'; import {allHavePendingRTERViolation, isDuplicate, isOnHold as isOnHoldTransactionUtils, shouldShowBrokenConnectionViolationForMultipleTransactions} from './TransactionUtils'; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index dcd4a216f838a..c38876b0a6896 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -105,6 +105,7 @@ import { getLoginsByAccountIDs, getPersonalDetailByEmail, getPersonalDetailsByIDs, + getLoginByAccountID, getShortMentionIfFound, } from './PersonalDetailsUtils'; import {addSMSDomainIfPhoneNumber} from './PhoneNumber'; @@ -10315,6 +10316,44 @@ function isExported(reportActions: OnyxEntry | ReportAction[]) { return Object.values(reportActions).some((action) => isExportIntegrationAction(action)); } +/** + * Determines whether the report can be moved to the workspace. + */ +const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report: OnyxEntry, oldPolicy: OnyxEntry, currentUserLogin: string | undefined): boolean => { + const isIOU = isIOUReport(report); + + if (isIOU && (currentUserAccountID !== report?.ownerAccountID || currentUserAccountID !== report?.managerID)) { + return false; + } + + const payerLogin = report?.managerID ? getLoginByAccountID(report?.managerID) : undefined; + if (isIOU && (!isPolicyAdminPolicyUtils(newPolicy, payerLogin) && newPolicy?.achAccount?.reimburser !== payerLogin)) { + return false; + } + + if (isIOU && report?.stateNum && report?.stateNum > CONST.REPORT.STATE_NUM.SUBMITTED) { + return false; + } + + const isCurrentUserMember = !!currentUserLogin && !!newPolicy?.employeeList?.[currentUserLogin]; + const isExpenseReportType = isExpenseReport(report); + if (isExpenseReportType && !isCurrentUserMember) { + return false; + } + + const isAdmin = isPolicyAdminPolicyUtils(newPolicy, currentUserLogin); + if (isExpenseReportType && report?.stateNum && report?.stateNum > CONST.REPORT.STATE_NUM.SUBMITTED && !isAdmin) { + return false; + } + + const reportActions = getAllReportActions(report?.reportID); + if (isExpenseReportType && isExported(reportActions)) { + return false; + } + + return true; +}; + function getApprovalChain(policy: OnyxEntry, expenseReport: OnyxEntry): string[] { const approvalChain: string[] = []; const fullApprovalChain: string[] = []; @@ -10841,6 +10880,7 @@ export { getOutstandingReports, isReportOutstanding, isAllowedToSubmitDraftExpenseReport, + isWorkspaceEligibleForReportChange, }; export type { diff --git a/src/pages/ReportChangeWorkspacePage.tsx b/src/pages/ReportChangeWorkspacePage.tsx index e6bd240f803b4..3b5137b74cd4f 100644 --- a/src/pages/ReportChangeWorkspacePage.tsx +++ b/src/pages/ReportChangeWorkspacePage.tsx @@ -16,8 +16,8 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportChangeWorkspaceNavigatorParamList} from '@libs/Navigation/types'; import {getLoginByAccountID} from '@libs/PersonalDetailsUtils'; -import {getPolicy, isPolicyAdmin, isPolicyMember, isWorkspaceEligibleForReportChange} from '@libs/PolicyUtils'; -import {isIOUReport, isMoneyRequestReport, isMoneyRequestReportPendingDeletion} from '@libs/ReportUtils'; +import {getPolicy, isPolicyAdmin, isPolicyMember} from '@libs/PolicyUtils'; +import {isIOUReport, isMoneyRequestReport, isMoneyRequestReportPendingDeletion, isWorkspaceEligibleForReportChange} from '@libs/ReportUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/tests/unit/PolicyUtilsTest.ts b/tests/unit/PolicyUtilsTest.ts index fdbe92c227de4..7a702b1f934f9 100644 --- a/tests/unit/PolicyUtilsTest.ts +++ b/tests/unit/PolicyUtilsTest.ts @@ -10,9 +10,9 @@ import { getSubmitToAccountID, getUnitRateValue, isUserInvitedToWorkspace, - isWorkspaceEligibleForReportChange, shouldShowPolicy, } from '@libs/PolicyUtils'; +import {isWorkspaceEligibleForReportChange} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Policy, PolicyEmployeeList, Report, Transaction} from '@src/types/onyx'; From 344c5219ca7d60a8ada1b3e47c7c7fdc0c52e6e9 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 25 Apr 2025 16:59:40 +0200 Subject: [PATCH 07/19] fix lint --- src/libs/ReportUtils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c38876b0a6896..88ef917ea99dd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -102,10 +102,10 @@ import { getAccountIDsByLogins, getDisplayNameOrDefault, getEffectiveDisplayName, + getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail, getPersonalDetailsByIDs, - getLoginByAccountID, getShortMentionIfFound, } from './PersonalDetailsUtils'; import {addSMSDomainIfPhoneNumber} from './PhoneNumber'; @@ -10327,7 +10327,7 @@ const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report } const payerLogin = report?.managerID ? getLoginByAccountID(report?.managerID) : undefined; - if (isIOU && (!isPolicyAdminPolicyUtils(newPolicy, payerLogin) && newPolicy?.achAccount?.reimburser !== payerLogin)) { + if (isIOU && !isPolicyAdminPolicyUtils(newPolicy, payerLogin) && newPolicy?.achAccount?.reimburser !== payerLogin) { return false; } @@ -10337,17 +10337,17 @@ const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report const isCurrentUserMember = !!currentUserLogin && !!newPolicy?.employeeList?.[currentUserLogin]; const isExpenseReportType = isExpenseReport(report); - if (isExpenseReportType && !isCurrentUserMember) { + if (!isCurrentUserMember) { return false; } const isAdmin = isPolicyAdminPolicyUtils(newPolicy, currentUserLogin); - if (isExpenseReportType && report?.stateNum && report?.stateNum > CONST.REPORT.STATE_NUM.SUBMITTED && !isAdmin) { + if (report?.stateNum && report?.stateNum > CONST.REPORT.STATE_NUM.SUBMITTED && !isAdmin) { return false; } const reportActions = getAllReportActions(report?.reportID); - if (isExpenseReportType && isExported(reportActions)) { + if (isExported(reportActions)) { return false; } From f2eda6f84d670767bc801f68914035698f56dd98 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 25 Apr 2025 17:10:17 +0200 Subject: [PATCH 08/19] rm unused code --- src/libs/ReportUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 88ef917ea99dd..77ab6157026d5 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10336,7 +10336,6 @@ const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report } const isCurrentUserMember = !!currentUserLogin && !!newPolicy?.employeeList?.[currentUserLogin]; - const isExpenseReportType = isExpenseReport(report); if (!isCurrentUserMember) { return false; } From d7eff0355cdad6c7e4554a3aa774e20f11a880f5 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 25 Apr 2025 17:54:00 +0200 Subject: [PATCH 09/19] improve logic --- src/libs/ReportUtils.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 77ab6157026d5..4f688866eba06 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10340,8 +10340,15 @@ const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report return false; } - const isAdmin = isPolicyAdminPolicyUtils(newPolicy, currentUserLogin); - if (report?.stateNum && report?.stateNum > CONST.REPORT.STATE_NUM.SUBMITTED && !isAdmin) { + const submitterLogin = report?.ownerAccountID ? getLoginByAccountID(report?.ownerAccountID) : undefined; + const isCurrentUserAdmin = isPolicyAdminPolicyUtils(newPolicy, currentUserLogin); + const isSubmitterMember = !!submitterLogin && !!newPolicy?.employeeList?.[submitterLogin]; + + if (!isSubmitterMember && !isCurrentUserAdmin) { + return false; + } + + if (report?.stateNum && report?.stateNum > CONST.REPORT.STATE_NUM.SUBMITTED && !isCurrentUserAdmin) { return false; } From be49947e19327ba46d55b2120da2f524372d713f Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 30 Apr 2025 18:43:33 +0200 Subject: [PATCH 10/19] refactor change workspace condition --- src/libs/PolicyUtils.ts | 2 +- src/libs/ReportSecondaryActionUtils.ts | 10 ++--- src/libs/ReportUtils.ts | 56 ++++++++++++++------------ 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index bba218f72a8cc..1066a58e4cce8 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -40,7 +40,7 @@ import DateUtils from './DateUtils'; import {translateLocal} from './Localize'; import Navigation from './Navigation/Navigation'; import {isOffline as isOfflineNetworkStore} from './Network/NetworkStore'; -import {getAccountIDsByLogins, getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils'; +import {getAccountIDsByLogins, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils'; import {getAllSortedTransactions, getCategory, getTag} from './TransactionUtils'; import {isPublicDomain} from './ValidationUtils'; diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 231a2b36aff9d..5adcef35567dd 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -3,7 +3,7 @@ import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import type {Policy, Report, ReportAction, Transaction, TransactionViolation} from '@src/types/onyx'; import {isApprover as isApproverUtils} from './actions/Policy/Member'; -import {getCurrentUserAccountID, getCurrentUserEmail} from './actions/Report'; +import {getCurrentUserAccountID} from './actions/Report'; import { arePaymentsEnabled as arePaymentsEnabledUtils, getAllPolicies, @@ -302,10 +302,10 @@ function isHoldActionForTransation(report: Report, reportTransaction: Transactio return isProcessingReport; } -function isChangeWorkspaceAction(report: Report, policy?: Policy): boolean { +function isChangeWorkspaceAction(report: Report): boolean { const policies = getAllPolicies(); - const currentUserEmail = getCurrentUserEmail(); - return policies.filter((newPolicy) => isWorkspaceEligibleForReportChange(newPolicy, report, policy, currentUserEmail)).length > 0; + const session = getSession(); + return policies.filter((newPolicy) => isWorkspaceEligibleForReportChange(newPolicy, report, session)).length > 0; } function isDeleteAction(report: Report, reportTransactions: Transaction[]): boolean { @@ -378,7 +378,7 @@ function getSecondaryReportActions( options.push(CONST.REPORT.SECONDARY_ACTIONS.DOWNLOAD); - if (isChangeWorkspaceAction(report, policy)) { + if (isChangeWorkspaceAction(report)) { options.push(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE); } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 166533fa49ead..c7237d0c57550 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -102,7 +102,6 @@ import { getAccountIDsByLogins, getDisplayNameOrDefault, getEffectiveDisplayName, - getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail, getPersonalDetailsByIDs, @@ -10282,48 +10281,55 @@ function isExported(reportActions: OnyxEntry | ReportAction[]) { return Object.values(reportActions).some((action) => isExportIntegrationAction(action)); } +function verifyState(report: OnyxEntry, validStates: Array>): boolean { + return !!report?.stateNum && validStates.includes(report?.stateNum); +} + +function verifyStatus(report: OnyxEntry, validStatuses: Array>): boolean { + return !!report?.statusNum && validStatuses.includes(report?.statusNum); +} + /** * Determines whether the report can be moved to the workspace. */ -const isWorkspaceEligibleForReportChange = (newPolicy: OnyxEntry, report: OnyxEntry, oldPolicy: OnyxEntry, currentUserLogin: string | undefined): boolean => { +function isWorkspaceEligibleForReportChange(newPolicy: OnyxEntry, report: OnyxEntry, session: OnyxEntry): boolean { const isIOU = isIOUReport(report); + const isCurrentUserMember = !!session?.email && !!newPolicy?.employeeList?.[session?.email]; + const isCurrentUserAdmin = isPolicyAdminPolicyUtils(newPolicy, session?.email); + const isPaidGroupPolicyType = isPaidGroupPolicyPolicyUtils(newPolicy); + const isReportOpenOrSubmitted = verifyState(report, [CONST.REPORT.STATE_NUM.OPEN, CONST.REPORT.STATE_NUM.SUBMITTED]); - if (isIOU && (currentUserAccountID !== report?.ownerAccountID || currentUserAccountID !== report?.managerID)) { - return false; - } - - const payerLogin = report?.managerID ? getLoginByAccountID(report?.managerID) : undefined; - if (isIOU && !isPolicyAdminPolicyUtils(newPolicy, payerLogin) && newPolicy?.achAccount?.reimburser !== payerLogin) { - return false; + if (isIOU && isReportOpenOrSubmitted && isPaidGroupPolicyType && (isCurrentUserMember || isCurrentUserAdmin)) { + return true; } - if (isIOU && report?.stateNum && report?.stateNum > CONST.REPORT.STATE_NUM.SUBMITTED) { + // From this point on, reports must be of type Expense and a member of a paid policy, so return early here otherwise. + const isExpenseReportType = isExpenseReport(report); + if (!isExpenseReportType || !isCurrentUserMember || !isPaidGroupPolicyType) { return false; } - const isCurrentUserMember = !!currentUserLogin && !!newPolicy?.employeeList?.[currentUserLogin]; - if (!isCurrentUserMember) { - return false; + // Expense report case + const isSubmitter = currentUserAccountID === report?.ownerAccountID; + if (isSubmitter && isReportOpenOrSubmitted) { + return true; } - const submitterLogin = report?.ownerAccountID ? getLoginByAccountID(report?.ownerAccountID) : undefined; - const isCurrentUserAdmin = isPolicyAdminPolicyUtils(newPolicy, currentUserLogin); - const isSubmitterMember = !!submitterLogin && !!newPolicy?.employeeList?.[submitterLogin]; - - if (!isSubmitterMember && !isCurrentUserAdmin) { - return false; + const isCurrentUserApprover = currentUserAccountID === report?.managerID; + if (isCurrentUserApprover && verifyState(report, [CONST.REPORT.STATE_NUM.SUBMITTED])) { + return true; } - if (report?.stateNum && report?.stateNum > CONST.REPORT.STATE_NUM.SUBMITTED && !isCurrentUserAdmin) { - return false; + const isCurrentUserPayer = isPayer(session, report, false, newPolicy); + if (isCurrentUserPayer && verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED])) { + return true; } - const reportActions = getAllReportActions(report?.reportID); - if (isExported(reportActions)) { - return false; + if (isCurrentUserAdmin && verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED]) && verifyStatus(report, [CONST.REPORT.STATUS_NUM.APPROVED, CONST.REPORT.STATUS_NUM.REIMBURSED, CONST.REPORT.STATUS_NUM.CLOSED])) { + return true; } - return true; + return false; }; function getApprovalChain(policy: OnyxEntry, expenseReport: OnyxEntry): string[] { From 81fbfc01613432461b4ef1f7ceb8c1e21dfb0629 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 30 Apr 2025 18:44:38 +0200 Subject: [PATCH 11/19] fix lint --- src/libs/ReportUtils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c7237d0c57550..9856b708b5144 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10325,12 +10325,16 @@ function isWorkspaceEligibleForReportChange(newPolicy: OnyxEntry, report return true; } - if (isCurrentUserAdmin && verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED]) && verifyStatus(report, [CONST.REPORT.STATUS_NUM.APPROVED, CONST.REPORT.STATUS_NUM.REIMBURSED, CONST.REPORT.STATUS_NUM.CLOSED])) { + if ( + isCurrentUserAdmin && + verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED]) && + verifyStatus(report, [CONST.REPORT.STATUS_NUM.APPROVED, CONST.REPORT.STATUS_NUM.REIMBURSED, CONST.REPORT.STATUS_NUM.CLOSED]) + ) { return true; } return false; -}; +} function getApprovalChain(policy: OnyxEntry, expenseReport: OnyxEntry): string[] { const approvalChain: string[] = []; From 9150a48c4e89b0bf0b0754530ff524ba229b1f8b Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 30 Apr 2025 22:03:51 +0200 Subject: [PATCH 12/19] update tests --- src/libs/ReportUtils.ts | 39 ++++++--- tests/unit/PolicyUtilsTest.ts | 149 +++++++++++++++++++++------------- 2 files changed, 117 insertions(+), 71 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9856b708b5144..3d30193d51fca 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -102,6 +102,7 @@ import { getAccountIDsByLogins, getDisplayNameOrDefault, getEffectiveDisplayName, + getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail, getPersonalDetailsByIDs, @@ -10282,11 +10283,17 @@ function isExported(reportActions: OnyxEntry | ReportAction[]) { } function verifyState(report: OnyxEntry, validStates: Array>): boolean { - return !!report?.stateNum && validStates.includes(report?.stateNum); + if (report?.stateNum === undefined || report?.stateNum === null) { + return false; + } + return validStates.includes(report?.stateNum); } function verifyStatus(report: OnyxEntry, validStatuses: Array>): boolean { - return !!report?.statusNum && validStatuses.includes(report?.statusNum); + if (report?.statusNum === undefined || report?.statusNum === null) { + return false; + } + return validStatuses.includes(report?.statusNum); } /** @@ -10294,34 +10301,40 @@ function verifyStatus(report: OnyxEntry, validStatuses: Array, report: OnyxEntry, session: OnyxEntry): boolean { const isIOU = isIOUReport(report); - const isCurrentUserMember = !!session?.email && !!newPolicy?.employeeList?.[session?.email]; + const submitterLogin = report?.ownerAccountID && getLoginByAccountID(report?.ownerAccountID); + const isSubmitterMember = !!submitterLogin && !!newPolicy?.employeeList?.[submitterLogin]; + const managerLogin = report?.managerID && getLoginByAccountID(report?.managerID); + const isManagerMember = !!managerLogin && !!newPolicy?.employeeList?.[managerLogin]; const isCurrentUserAdmin = isPolicyAdminPolicyUtils(newPolicy, session?.email); const isPaidGroupPolicyType = isPaidGroupPolicyPolicyUtils(newPolicy); const isReportOpenOrSubmitted = verifyState(report, [CONST.REPORT.STATE_NUM.OPEN, CONST.REPORT.STATE_NUM.SUBMITTED]); - if (isIOU && isReportOpenOrSubmitted && isPaidGroupPolicyType && (isCurrentUserMember || isCurrentUserAdmin)) { + // For IOUs, the sender and receiver can only change the workspace if: + // 1. The sender AND receiver are both members of the new policy OR + // 2. The sender OR receiver is an admin of the new policy. In this case, changing the policy also invites the non-member to the policy + if (isIOU && isReportOpenOrSubmitted && isPaidGroupPolicyType && ((isSubmitterMember && isManagerMember) || isCurrentUserAdmin)) { return true; } - // From this point on, reports must be of type Expense and a member of a paid policy, so return early here otherwise. + // From this point on, reports must be of type Expense, the policy must be a paid type. + // The submitter and manager must also be policy members OR the current user is an admin so they can invite the non-members to the policy. const isExpenseReportType = isExpenseReport(report); - if (!isExpenseReportType || !isCurrentUserMember || !isPaidGroupPolicyType) { + if (!isExpenseReportType || !isPaidGroupPolicyType || !((isSubmitterMember && isManagerMember) || isCurrentUserAdmin)) { return false; } - // Expense report case - const isSubmitter = currentUserAccountID === report?.ownerAccountID; - if (isSubmitter && isReportOpenOrSubmitted) { + const isCurrentUserReportSubmitter = session?.accountID === report?.ownerAccountID; + if (isCurrentUserReportSubmitter && isReportOpenOrSubmitted) { return true; } - const isCurrentUserApprover = currentUserAccountID === report?.managerID; - if (isCurrentUserApprover && verifyState(report, [CONST.REPORT.STATE_NUM.SUBMITTED])) { + const isCurrentUserReportApprover = session?.accountID === report?.managerID; + if (isCurrentUserReportApprover && verifyState(report, [CONST.REPORT.STATE_NUM.SUBMITTED])) { return true; } - const isCurrentUserPayer = isPayer(session, report, false, newPolicy); - if (isCurrentUserPayer && verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED])) { + const isCurrentUserReportPayer = isPayer(session, report, false, newPolicy); + if (isCurrentUserReportPayer && verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED])) { return true; } diff --git a/tests/unit/PolicyUtilsTest.ts b/tests/unit/PolicyUtilsTest.ts index 7a702b1f934f9..31f980a70d686 100644 --- a/tests/unit/PolicyUtilsTest.ts +++ b/tests/unit/PolicyUtilsTest.ts @@ -644,134 +644,166 @@ describe('PolicyUtils', () => { await waitForBatchedUpdatesWithAct(); }); - it('returns false if current user is not a member of the new policy', async () => { + it('returns true if report is IOU and both submitter and receiver are members of the policy', () => { + const currentUserLogin = employeeEmail; + const currentUserAccountID = employeeAccountID; + const session = {email: currentUserLogin, accountID: currentUserAccountID}; + + const newPolicy = { + ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), + employeeList: { + [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.USER}, + [approverEmail]: {email: approverEmail, role: CONST.POLICY.ROLE.USER}, + }, + }; + const report = { + ...createRandomReport(0), + type: CONST.REPORT.TYPE.IOU, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + ownerAccountID: currentUserAccountID, + managerID: approverAccountID, + }; + + const result = isWorkspaceEligibleForReportChange(newPolicy, report, session); + expect(result).toBe(true); + }); + + it('returns true if report is IOU and the submitter is not a member but the current user is a policy admin', () => { + const currentUserLogin = adminEmail; + const currentUserAccountID = adminAccountID; + const session = {email: currentUserLogin, accountID: currentUserAccountID}; + + const newPolicy = { + ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), + role: CONST.POLICY.ROLE.ADMIN, + employeeList: { + [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.ADMIN}, + }, + }; + const report = { + ...createRandomReport(0), + type: CONST.REPORT.TYPE.IOU, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + ownerAccountID: employeeAccountID, + }; + + const result = isWorkspaceEligibleForReportChange(newPolicy, report, session); + expect(result).toBe(true); + }); + + it('returns false if current user is not a member of the new policy', () => { const newPolicy = { ...createRandomPolicy(1), employeeList: {}, }; const report = createRandomReport(0); - const oldPolicy = createRandomPolicy(0); const currentUserLogin = 'nonmember@tests.com'; - await Onyx.set(ONYXKEYS.SESSION, {email: currentUserLogin, accountID: 0}); + const session = {email: currentUserLogin, accountID: 0}; - const result = isWorkspaceEligibleForReportChange(newPolicy, report, oldPolicy, currentUserLogin); + const result = isWorkspaceEligibleForReportChange(newPolicy, report, session); expect(result).toBe(false); }); - it('returns true if current user is the submitter', async () => { + it('returns true if current user is the submitter', () => { const currentUserLogin = employeeEmail; const currentUserAccountID = employeeAccountID; - await Onyx.set(ONYXKEYS.SESSION, {email: currentUserLogin, accountID: currentUserAccountID}); + const session = {email: currentUserLogin, accountID: currentUserAccountID}; const newPolicy = { - ...createRandomPolicy(1), + ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), employeeList: { [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.USER}, + [approverEmail]: {email: approverEmail, role: CONST.POLICY.ROLE.USER}, }, }; - const oldPolicy = createRandomPolicy(0); const report = { ...createRandomReport(0), + type: CONST.REPORT.TYPE.EXPENSE, + stateNum: CONST.REPORT.STATE_NUM.OPEN, ownerAccountID: currentUserAccountID, + managerID: approverAccountID, }; - const result = isWorkspaceEligibleForReportChange(newPolicy, report, oldPolicy, currentUserLogin); + const result = isWorkspaceEligibleForReportChange(newPolicy, report, session); expect(result).toBe(true); }); - it('returns true if current user is a policy admin', async () => { + it('returns true if current user is a policy admin and the report is processing', () => { const currentUserLogin = adminEmail; const currentUserAccountID = adminAccountID; - await Onyx.set(ONYXKEYS.SESSION, {email: currentUserLogin, accountID: currentUserAccountID}); + const session = {email: currentUserLogin, accountID: currentUserAccountID}; const newPolicy = { - ...createRandomPolicy(1), + ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), + role: CONST.POLICY.ROLE.ADMIN, employeeList: { [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.ADMIN}, }, }; - const oldPolicy = createRandomPolicy(0); - const report = createRandomReport(0); - - const result = isWorkspaceEligibleForReportChange(newPolicy, report, oldPolicy, currentUserLogin); - expect(result).toBe(true); - }); - - it('returns true if current user is the policy owner', async () => { - const currentUserLogin = 'owner@test.com'; - const currentUserAccountID = ownerAccountID; - await Onyx.set(ONYXKEYS.SESSION, {email: currentUserLogin, accountID: currentUserAccountID}); - - const newPolicy = { - ...createRandomPolicy(1), + const report = { + ...createRandomReport(0), + type: CONST.REPORT.TYPE.EXPENSE, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, ownerAccountID: currentUserAccountID, - employeeList: { - [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.ADMIN}, - }, }; - const oldPolicy = createRandomPolicy(0); - const report = createRandomReport(0); - const result = isWorkspaceEligibleForReportChange(newPolicy, report, oldPolicy, currentUserLogin); + const result = isWorkspaceEligibleForReportChange(newPolicy, report, session); expect(result).toBe(true); }); - it('returns true if current user is the approver and submitter is a member', async () => { + it('returns true if current user is the approver and submitter is a member', () => { const currentUserLogin = approverEmail; const currentUserAccountID = approverAccountID; - await Onyx.set(ONYXKEYS.SESSION, {email: currentUserLogin, accountID: currentUserAccountID}); + const session = {email: currentUserLogin, accountID: currentUserAccountID}; const submitterLogin = employeeEmail; const submitterAccountID = employeeAccountID; const newPolicy = { - ...createRandomPolicy(1), + ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), employeeList: { [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.USER}, [submitterLogin]: {email: submitterLogin, role: CONST.POLICY.ROLE.USER}, }, }; - const oldPolicy = { - ...createRandomPolicy(0), - employeeList: { - [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.USER}, - [submitterLogin]: {email: submitterLogin, role: CONST.POLICY.ROLE.USER, submitsTo: currentUserLogin}, - }, - approver: currentUserLogin, - }; const report = { ...createRandomReport(0), + type: CONST.REPORT.TYPE.EXPENSE, + managerID: approverAccountID, ownerAccountID: submitterAccountID, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, }; - const result = isWorkspaceEligibleForReportChange(newPolicy, report, oldPolicy, currentUserLogin); + const result = isWorkspaceEligibleForReportChange(newPolicy, report, session); expect(result).toBe(true); }); - it('returns false if current user is approver but submitter not member', async () => { + it('returns false if current user is approver but submitter not member', () => { const currentUserLogin = approverEmail; const currentUserAccountID = approverAccountID; - await Onyx.set(ONYXKEYS.SESSION, {email: currentUserLogin, accountID: currentUserAccountID}); + const session = {email: currentUserLogin, accountID: currentUserAccountID}; const newPolicy = { - ...createRandomPolicy(1), + ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), employeeList: { [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.USER}, }, }; const report = { ...createRandomReport(0), + type: CONST.REPORT.TYPE.EXPENSE, + managerID: approverAccountID, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, ownerAccountID: employeeAccountID, }; - const oldPolicy = createRandomPolicy(0); - expect(isWorkspaceEligibleForReportChange(newPolicy, report, oldPolicy, currentUserLogin)).toBe(false); + expect(isWorkspaceEligibleForReportChange(newPolicy, report, session)).toBe(false); }); - it('returns false if the report is approved and the current user is not an admin', async () => { + it('returns false if the report is approved and the current user is not an admin', () => { const currentUserLogin = approverEmail; const currentUserAccountID = approverAccountID; - await Onyx.set(ONYXKEYS.SESSION, {email: currentUserLogin, accountID: currentUserAccountID}); + const session = {email: currentUserLogin, accountID: currentUserAccountID}; const newPolicy = { ...createRandomPolicy(1), @@ -784,18 +816,18 @@ describe('PolicyUtils', () => { ownerAccountID: employeeAccountID, stateNum: CONST.REPORT.STATE_NUM.APPROVED, }; - const oldPolicy = createRandomPolicy(0); - expect(isWorkspaceEligibleForReportChange(newPolicy, report, oldPolicy, currentUserLogin)).toBe(false); + expect(isWorkspaceEligibleForReportChange(newPolicy, report, session)).toBe(false); }); - it('returns true if the report is approved and the current user is an admin', async () => { + it('returns true if the report is approved and the current user is an admin', () => { const currentUserLogin = adminEmail; const currentUserAccountID = adminAccountID; - await Onyx.set(ONYXKEYS.SESSION, {email: currentUserLogin, accountID: currentUserAccountID}); + const session = {email: currentUserLogin, accountID: currentUserAccountID}; const newPolicy = { - ...createRandomPolicy(1), + ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), + role: CONST.POLICY.ROLE.ADMIN, employeeList: { [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.ADMIN}, }, @@ -804,10 +836,11 @@ describe('PolicyUtils', () => { ...createRandomReport(0), ownerAccountID: employeeAccountID, stateNum: CONST.REPORT.STATE_NUM.APPROVED, + type: CONST.REPORT.TYPE.EXPENSE, + managerID: approverAccountID, }; - const oldPolicy = createRandomPolicy(0); - expect(isWorkspaceEligibleForReportChange(newPolicy, report, oldPolicy, currentUserLogin)).toBe(true); + expect(isWorkspaceEligibleForReportChange(newPolicy, report, session)).toBe(true); }); }); From 6357764d9df884acf74d2ac322e7f6635c8514a9 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 30 Apr 2025 23:12:28 +0200 Subject: [PATCH 13/19] add more tests --- src/libs/ReportUtils.ts | 14 +- tests/unit/PolicyUtilsTest.ts | 1 + tests/unit/ReportSecondaryActionUtilsTest.ts | 291 ++++++++++++------- 3 files changed, 201 insertions(+), 105 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3d30193d51fca..2d6ad13ba91aa 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -71,6 +71,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import {createDraftTransaction, getIOUReportActionToApproveOrPay, setMoneyRequestParticipants, unholdRequest} from './actions/IOU'; import {createDraftWorkspace} from './actions/Policy/Policy'; +import {isApprover as isApproverMember} from './actions/Policy/Member'; import {autoSwitchToFocusMode} from './actions/PriorityMode'; import {hasCreditBankAccount} from './actions/ReimbursementAccount/store'; import {handleReportChanged} from './actions/Report'; @@ -10300,6 +10301,10 @@ function verifyStatus(report: OnyxEntry, validStatuses: Array, report: OnyxEntry, session: OnyxEntry): boolean { + if (!session?.accountID) { + return false; + } + const isIOU = isIOUReport(report); const submitterLogin = report?.ownerAccountID && getLoginByAccountID(report?.ownerAccountID); const isSubmitterMember = !!submitterLogin && !!newPolicy?.employeeList?.[submitterLogin]; @@ -10313,6 +10318,7 @@ function isWorkspaceEligibleForReportChange(newPolicy: OnyxEntry, report // 1. The sender AND receiver are both members of the new policy OR // 2. The sender OR receiver is an admin of the new policy. In this case, changing the policy also invites the non-member to the policy if (isIOU && isReportOpenOrSubmitted && isPaidGroupPolicyType && ((isSubmitterMember && isManagerMember) || isCurrentUserAdmin)) { + console.log('over here 1'); return true; } @@ -10323,18 +10329,21 @@ function isWorkspaceEligibleForReportChange(newPolicy: OnyxEntry, report return false; } - const isCurrentUserReportSubmitter = session?.accountID === report?.ownerAccountID; + const isCurrentUserReportSubmitter = session.accountID === report?.ownerAccountID; if (isCurrentUserReportSubmitter && isReportOpenOrSubmitted) { + console.log('over here 2'); return true; } - const isCurrentUserReportApprover = session?.accountID === report?.managerID; + const isCurrentUserReportApprover = isApproverMember(newPolicy, session.accountID); if (isCurrentUserReportApprover && verifyState(report, [CONST.REPORT.STATE_NUM.SUBMITTED])) { + console.log('over here 3'); return true; } const isCurrentUserReportPayer = isPayer(session, report, false, newPolicy); if (isCurrentUserReportPayer && verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED])) { + console.log('over here 4'); return true; } @@ -10343,6 +10352,7 @@ function isWorkspaceEligibleForReportChange(newPolicy: OnyxEntry, report verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED]) && verifyStatus(report, [CONST.REPORT.STATUS_NUM.APPROVED, CONST.REPORT.STATUS_NUM.REIMBURSED, CONST.REPORT.STATUS_NUM.CLOSED]) ) { + console.log('over here 5'); return true; } diff --git a/tests/unit/PolicyUtilsTest.ts b/tests/unit/PolicyUtilsTest.ts index 31f980a70d686..f0c51d3e0e9c7 100644 --- a/tests/unit/PolicyUtilsTest.ts +++ b/tests/unit/PolicyUtilsTest.ts @@ -761,6 +761,7 @@ describe('PolicyUtils', () => { const newPolicy = { ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), + approver: approverEmail, employeeList: { [currentUserLogin]: {email: currentUserLogin, role: CONST.POLICY.ROLE.USER}, [submitterLogin]: {email: submitterLogin, role: CONST.POLICY.ROLE.USER}, diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index 15d91fe4df8bb..f460fac9e3116 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -4,22 +4,29 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Report, ReportAction, Transaction, TransactionViolation} from '@src/types/onyx'; -const CURRENT_USER_ACCOUNT_ID = 1; -const CURRENT_USER_EMAIL = 'tester@mail.com'; +const EMPLOYEE_ACCOUNT_ID = 1; +const EMPLOYEE_EMAIL = 'employee@mail.com'; +const MANAGER_ACCOUNT_ID = 2; +const MANAGER_EMAIL = 'manager@mail.com'; +const APPROVER_ACCOUNT_ID = 3; +const APPROVER_EMAIL = 'approver@mail.com'; +const PAYER_ACCOUNT_ID = 4; +const PAYER_EMAIL = 'payer@mail.com'; +const ADMIN_ACCOUNT_ID = 5; +const ADMIN_EMAIL = 'admin@mail.com'; const SESSION = { - email: CURRENT_USER_EMAIL, - accountID: CURRENT_USER_ACCOUNT_ID, + email: EMPLOYEE_EMAIL, + accountID: EMPLOYEE_ACCOUNT_ID, }; const PERSONAL_DETAILS = { - accountID: CURRENT_USER_ACCOUNT_ID, - login: CURRENT_USER_EMAIL, + accountID: EMPLOYEE_ACCOUNT_ID, + login: EMPLOYEE_EMAIL, }; const REPORT_ID = 1; const POLICY_ID = 'POLICY_ID'; -const POLICY_ID2 = 'POLICY_ID2'; describe('getSecondaryAction', () => { beforeAll(() => { @@ -32,7 +39,7 @@ describe('getSecondaryAction', () => { jest.clearAllMocks(); Onyx.clear(); await Onyx.merge(ONYXKEYS.SESSION, SESSION); - await Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, {[CURRENT_USER_ACCOUNT_ID]: PERSONAL_DETAILS}); + await Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, {[EMPLOYEE_ACCOUNT_ID]: PERSONAL_DETAILS}); }); it('should always return default options', () => { @@ -47,7 +54,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.OPEN, statusNum: CONST.REPORT.STATUS_NUM.OPEN, } as unknown as Report; @@ -67,12 +74,12 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, } as unknown as Report; const policy = { - approver: CURRENT_USER_EMAIL, + approver: EMPLOYEE_EMAIL, } as unknown as Policy; const TRANSACTION_ID = 'TRANSACTION_ID'; const transaction = { @@ -97,7 +104,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, } as unknown as Report; const policy = {} as unknown as Policy; const TRANSACTION_ID = 'TRANSACTION_ID'; @@ -122,7 +129,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.OPEN, statusNum: CONST.REPORT.STATUS_NUM.OPEN, } as unknown as Report; @@ -148,11 +155,11 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.APPROVED, } as unknown as Report; - const policy = {approver: CURRENT_USER_EMAIL} as unknown as Policy; + const policy = {approver: EMPLOYEE_EMAIL} as unknown as Policy; const result = getSecondaryReportActions(report, [], {}, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.UNAPPROVE)).toBe(true); @@ -162,7 +169,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED, } as unknown as Report; @@ -178,7 +185,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, statusNum: CONST.REPORT.STATUS_NUM.APPROVED, isWaitingOnBankAccount: true, } as unknown as Report; @@ -215,7 +222,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.INVOICE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, } as unknown as Report; const policy = { connections: { @@ -232,7 +239,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.APPROVED, } as unknown as Report; @@ -250,7 +257,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.APPROVED, } as unknown as Report; @@ -267,7 +274,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.INVOICE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, } as unknown as Report; const policy = { connections: {[CONST.POLICY.CONNECTIONS.NAME.QBD]: {}}, @@ -282,7 +289,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.APPROVED, } as unknown as Report; @@ -300,7 +307,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.APPROVED, } as unknown as Report; @@ -317,12 +324,12 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.APPROVED, } as unknown as Report; const policy = { - connections: {[CONST.POLICY.CONNECTIONS.NAME.QBD]: {config: {export: {exporter: CURRENT_USER_EMAIL}, autoSync: {enabled: true}}}}, + connections: {[CONST.POLICY.CONNECTIONS.NAME.QBD]: {config: {export: {exporter: EMPLOYEE_EMAIL}, autoSync: {enabled: true}}}}, } as unknown as Policy; const result = getSecondaryReportActions(report, [], {}, policy); @@ -333,7 +340,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, } as unknown as Report; @@ -347,27 +354,59 @@ describe('getSecondaryAction', () => { expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.HOLD)).toBe(true); }); - it('includes CHANGE_WORKSPACE option for closed expense report submitter', async () => { + it('includes CHANGE_WORKSPACE option for IOU and both submitter and payer members', async () => { const report = { reportID: REPORT_ID, - type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + type: CONST.REPORT.TYPE.IOU, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, + managerID: MANAGER_ACCOUNT_ID, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, } as unknown as Report; - + const personalDetails = { + [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, + [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, + } const policy = { id: POLICY_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, - areWorkflowsEnabled: false, + type: CONST.POLICY.TYPE.TEAM, + employeeList: { + [EMPLOYEE_EMAIL]: {email: EMPLOYEE_EMAIL, role: CONST.POLICY.ROLE.USER}, + [MANAGER_EMAIL]: {email: MANAGER_EMAIL, role: CONST.POLICY.ROLE.USER} + }, } as unknown as Policy; - const policy2 = { - id: POLICY_ID2, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, - }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); + await Onyx.merge(ONYXKEYS.SESSION, {email: EMPLOYEE_EMAIL, accountID: EMPLOYEE_ACCOUNT_ID}); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails); + + const result = getSecondaryReportActions(report, [], {}, policy); + expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE)).toBe(true); + }); + + it('includes CHANGE_WORKSPACE option for IOU and the submitter is not a member but current user is admin', async () => { + const report = { + reportID: REPORT_ID, + type: CONST.REPORT.TYPE.IOU, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, + managerID: MANAGER_ACCOUNT_ID, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + } as unknown as Report; + const personalDetails = { + [ADMIN_ACCOUNT_ID]: {login: ADMIN_EMAIL}, + [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, + } + const policy = { + id: POLICY_ID, + type: CONST.POLICY.TYPE.TEAM, + role: CONST.POLICY.ROLE.ADMIN, + employeeList: { + [ADMIN_EMAIL]: {email: ADMIN_EMAIL, role: CONST.POLICY.ROLE.ADMIN}, + }, + } as unknown as Policy; await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID2}`, policy2); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); + await Onyx.merge(ONYXKEYS.SESSION, {email: EMPLOYEE_EMAIL, accountID: EMPLOYEE_ACCOUNT_ID}); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails); const result = getSecondaryReportActions(report, [], {}, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE)).toBe(true); @@ -377,127 +416,173 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, statusNum: CONST.REPORT.STATUS_NUM.OPEN, stateNum: CONST.REPORT.STATE_NUM.OPEN, } as unknown as Report; + + const personalDetails = { + [ADMIN_ACCOUNT_ID]: {login: ADMIN_EMAIL}, + [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, + } + const policy = { id: POLICY_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, + type: CONST.POLICY.TYPE.TEAM, + role: CONST.POLICY.ROLE.ADMIN, + employeeList: { + [ADMIN_EMAIL]: {email: ADMIN_EMAIL, role: CONST.POLICY.ROLE.ADMIN}, + }, } as unknown as Policy; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); - - const policy2 = { - id: POLICY_ID2, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID2}`, policy2); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); + await Onyx.merge(ONYXKEYS.SESSION, {email: EMPLOYEE_EMAIL, accountID: EMPLOYEE_ACCOUNT_ID}); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails); const result = getSecondaryReportActions(report, [], {}, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE)).toBe(true); }); - it('includes CHANGE_WORKSPACE option for opened expense report submitter', async () => { + it('includes CHANGE_WORKSPACE option for submitter', async () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, + managerID: MANAGER_ACCOUNT_ID, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, } as unknown as Report; + + const personalDetails = { + [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, + [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, + } + const policy = { id: POLICY_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, - approver: CURRENT_USER_EMAIL, + type: CONST.POLICY.TYPE.TEAM, + employeeList: { + [MANAGER_EMAIL]: {email: MANAGER_EMAIL, role: CONST.POLICY.ROLE.USER}, + [EMPLOYEE_EMAIL]: {email: EMPLOYEE_EMAIL, role: CONST.POLICY.ROLE.USER}, + }, } as unknown as Policy; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); - const policy2 = { - id: POLICY_ID2, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID2}`, policy2); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); + await Onyx.merge(ONYXKEYS.SESSION, {email: EMPLOYEE_EMAIL, accountID: EMPLOYEE_ACCOUNT_ID}); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails); const result = getSecondaryReportActions(report, [], {}, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE)).toBe(true); }); - it('includes CHANGE_WORKSPACE option for approved expense report payer', async () => { + it('includes CHANGE_WORKSPACE option for approver', async () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - statusNum: CONST.REPORT.STATUS_NUM.APPROVED, - stateNum: CONST.REPORT.STATE_NUM.APPROVED, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, + managerID: MANAGER_ACCOUNT_ID, + statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, } as unknown as Report; + + const personalDetails = { + [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, + [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, + [APPROVER_ACCOUNT_ID]: {login: APPROVER_EMAIL}, + } + const policy = { id: POLICY_ID, - employeeList: {[CURRENT_USER_EMAIL]: {role: CONST.POLICY.ROLE.ADMIN}}, - role: CONST.POLICY.ROLE.ADMIN, + type: CONST.POLICY.TYPE.TEAM, + approver: APPROVER_EMAIL, + employeeList: { + [MANAGER_EMAIL]: {email: MANAGER_EMAIL, role: CONST.POLICY.ROLE.USER}, + [EMPLOYEE_EMAIL]: {email: EMPLOYEE_EMAIL, role: CONST.POLICY.ROLE.USER}, + }, } as unknown as Policy; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); - const policy2 = { - id: POLICY_ID2, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - employeeList: {[CURRENT_USER_EMAIL]: {role: CONST.POLICY.ROLE.ADMIN}}, - role: CONST.POLICY.ROLE.ADMIN, - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID2}`, policy2); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); + await Onyx.merge(ONYXKEYS.SESSION, {email: APPROVER_EMAIL, accountID: APPROVER_ACCOUNT_ID}); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails); const result = getSecondaryReportActions(report, [], {}, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE)).toBe(true); }); - it('includes CHANGE_WORKSPACE option for not exported expense report admin', async () => { + it('includes CHANGE_WORKSPACE option for payer', async () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, + managerID: MANAGER_ACCOUNT_ID, + statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, } as unknown as Report; + + const personalDetails = { + [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, + [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, + [APPROVER_ACCOUNT_ID]: {login: APPROVER_EMAIL}, + } + const policy = { id: POLICY_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, - role: CONST.POLICY.ROLE.ADMIN, + type: CONST.POLICY.TYPE.TEAM, + approver: APPROVER_EMAIL, + reimbursementChoice: CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES, + // achAccount: { + // reimburser: PAYER_EMAIL, + // }, + employeeList: { + [MANAGER_EMAIL]: {email: MANAGER_EMAIL, role: CONST.POLICY.ROLE.USER}, + [EMPLOYEE_EMAIL]: {email: EMPLOYEE_EMAIL, role: CONST.POLICY.ROLE.USER}, + [PAYER_EMAIL]: {email: PAYER_EMAIL, role: CONST.POLICY.ROLE.USER}, + }, } as unknown as Policy; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); - const policy2 = { - id: POLICY_ID2, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID2}`, policy2); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); + await Onyx.merge(ONYXKEYS.SESSION, {email: PAYER_EMAIL, accountID: PAYER_ACCOUNT_ID}); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails); const result = getSecondaryReportActions(report, [], {}, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE)).toBe(true); }); - it('includes CHANGE_WORKSPACE option for IOU report receiver', async () => { + it('includes CHANGE_WORKSPACE option for admin', async () => { const report = { reportID: REPORT_ID, - type: CONST.REPORT.TYPE.IOU, - managerID: CURRENT_USER_ACCOUNT_ID, - statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED, + type: CONST.REPORT.TYPE.EXPENSE, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, + managerID: MANAGER_ACCOUNT_ID, + statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, } as unknown as Report; + + const personalDetails = { + [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, + [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, + [APPROVER_ACCOUNT_ID]: {login: APPROVER_EMAIL}, + [ADMIN_ACCOUNT_ID]: {login: ADMIN_EMAIL}, + } + const policy = { id: POLICY_ID, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, + type: CONST.POLICY.TYPE.TEAM, + approver: APPROVER_EMAIL, + role: CONST.POLICY.ROLE.ADMIN, + employeeList: { + [ADMIN_EMAIL]: {email: ADMIN_EMAIL, role: CONST.POLICY.ROLE.ADMIN}, + }, } as unknown as Policy; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); - const policy2 = { - id: POLICY_ID2, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, - employeeList: {[CURRENT_USER_EMAIL]: {}}, - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID2}`, policy2); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); + await Onyx.merge(ONYXKEYS.SESSION, {email: ADMIN_EMAIL, accountID: ADMIN_ACCOUNT_ID}); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails); const result = getSecondaryReportActions(report, [], {}, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE)).toBe(true); @@ -507,7 +592,7 @@ describe('getSecondaryAction', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, statusNum: CONST.REPORT.STATUS_NUM.OPEN, stateNum: CONST.REPORT.STATE_NUM.OPEN, } as unknown as Report; @@ -530,7 +615,7 @@ describe('getSecondaryTransactionThreadActions', () => { jest.clearAllMocks(); Onyx.clear(); await Onyx.merge(ONYXKEYS.SESSION, SESSION); - await Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, {[CURRENT_USER_ACCOUNT_ID]: PERSONAL_DETAILS}); + await Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, {[EMPLOYEE_ACCOUNT_ID]: PERSONAL_DETAILS}); }); it('should always return VIEW_DETAILS', () => { @@ -544,7 +629,7 @@ describe('getSecondaryTransactionThreadActions', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, } as unknown as Report; @@ -561,7 +646,7 @@ describe('getSecondaryTransactionThreadActions', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: CURRENT_USER_ACCOUNT_ID, + ownerAccountID: EMPLOYEE_ACCOUNT_ID, statusNum: CONST.REPORT.STATUS_NUM.OPEN, stateNum: CONST.REPORT.STATE_NUM.OPEN, } as unknown as Report; From f6ab2933a154a030f50b6f2c5955b6d8d41f87b3 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 30 Apr 2025 23:19:21 +0200 Subject: [PATCH 14/19] fix tests --- src/libs/ReportUtils.ts | 5 ----- tests/unit/ReportSecondaryActionUtilsTest.ts | 7 ++----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2d6ad13ba91aa..b39660893c0f7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10318,7 +10318,6 @@ function isWorkspaceEligibleForReportChange(newPolicy: OnyxEntry, report // 1. The sender AND receiver are both members of the new policy OR // 2. The sender OR receiver is an admin of the new policy. In this case, changing the policy also invites the non-member to the policy if (isIOU && isReportOpenOrSubmitted && isPaidGroupPolicyType && ((isSubmitterMember && isManagerMember) || isCurrentUserAdmin)) { - console.log('over here 1'); return true; } @@ -10331,19 +10330,16 @@ function isWorkspaceEligibleForReportChange(newPolicy: OnyxEntry, report const isCurrentUserReportSubmitter = session.accountID === report?.ownerAccountID; if (isCurrentUserReportSubmitter && isReportOpenOrSubmitted) { - console.log('over here 2'); return true; } const isCurrentUserReportApprover = isApproverMember(newPolicy, session.accountID); if (isCurrentUserReportApprover && verifyState(report, [CONST.REPORT.STATE_NUM.SUBMITTED])) { - console.log('over here 3'); return true; } const isCurrentUserReportPayer = isPayer(session, report, false, newPolicy); if (isCurrentUserReportPayer && verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED])) { - console.log('over here 4'); return true; } @@ -10352,7 +10348,6 @@ function isWorkspaceEligibleForReportChange(newPolicy: OnyxEntry, report verifyState(report, [CONST.REPORT.STATE_NUM.APPROVED]) && verifyStatus(report, [CONST.REPORT.STATUS_NUM.APPROVED, CONST.REPORT.STATUS_NUM.REIMBURSED, CONST.REPORT.STATUS_NUM.CLOSED]) ) { - console.log('over here 5'); return true; } diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index f460fac9e3116..6e3564974170c 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -558,21 +558,18 @@ describe('getSecondaryAction', () => { type: CONST.REPORT.TYPE.EXPENSE, ownerAccountID: EMPLOYEE_ACCOUNT_ID, managerID: MANAGER_ACCOUNT_ID, - statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, - stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, } as unknown as Report; const personalDetails = { [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, - [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, - [APPROVER_ACCOUNT_ID]: {login: APPROVER_EMAIL}, [ADMIN_ACCOUNT_ID]: {login: ADMIN_EMAIL}, } const policy = { id: POLICY_ID, type: CONST.POLICY.TYPE.TEAM, - approver: APPROVER_EMAIL, role: CONST.POLICY.ROLE.ADMIN, employeeList: { [ADMIN_EMAIL]: {email: ADMIN_EMAIL, role: CONST.POLICY.ROLE.ADMIN}, From eaf06e247d06fc26f33b5324c1af6d6bdf433ab4 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 30 Apr 2025 23:20:35 +0200 Subject: [PATCH 15/19] fix lint --- src/libs/ReportUtils.ts | 2 +- tests/unit/ReportSecondaryActionUtilsTest.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b39660893c0f7..9f00b130c12ba 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -70,8 +70,8 @@ import type {Comment, TransactionChanges, WaypointCollection} from '@src/types/o import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import {createDraftTransaction, getIOUReportActionToApproveOrPay, setMoneyRequestParticipants, unholdRequest} from './actions/IOU'; -import {createDraftWorkspace} from './actions/Policy/Policy'; import {isApprover as isApproverMember} from './actions/Policy/Member'; +import {createDraftWorkspace} from './actions/Policy/Policy'; import {autoSwitchToFocusMode} from './actions/PriorityMode'; import {hasCreditBankAccount} from './actions/ReimbursementAccount/store'; import {handleReportChanged} from './actions/Report'; diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index 6e3564974170c..813088c4ddabf 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -365,13 +365,13 @@ describe('getSecondaryAction', () => { const personalDetails = { [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, - } + }; const policy = { id: POLICY_ID, type: CONST.POLICY.TYPE.TEAM, employeeList: { [EMPLOYEE_EMAIL]: {email: EMPLOYEE_EMAIL, role: CONST.POLICY.ROLE.USER}, - [MANAGER_EMAIL]: {email: MANAGER_EMAIL, role: CONST.POLICY.ROLE.USER} + [MANAGER_EMAIL]: {email: MANAGER_EMAIL, role: CONST.POLICY.ROLE.USER}, }, } as unknown as Policy; await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); @@ -394,7 +394,7 @@ describe('getSecondaryAction', () => { const personalDetails = { [ADMIN_ACCOUNT_ID]: {login: ADMIN_EMAIL}, [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, - } + }; const policy = { id: POLICY_ID, type: CONST.POLICY.TYPE.TEAM, @@ -424,7 +424,7 @@ describe('getSecondaryAction', () => { const personalDetails = { [ADMIN_ACCOUNT_ID]: {login: ADMIN_EMAIL}, [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, - } + }; const policy = { id: POLICY_ID, @@ -457,7 +457,7 @@ describe('getSecondaryAction', () => { const personalDetails = { [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, - } + }; const policy = { id: POLICY_ID, @@ -491,7 +491,7 @@ describe('getSecondaryAction', () => { [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, [APPROVER_ACCOUNT_ID]: {login: APPROVER_EMAIL}, - } + }; const policy = { id: POLICY_ID, @@ -526,7 +526,7 @@ describe('getSecondaryAction', () => { [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, [MANAGER_ACCOUNT_ID]: {login: MANAGER_EMAIL}, [APPROVER_ACCOUNT_ID]: {login: APPROVER_EMAIL}, - } + }; const policy = { id: POLICY_ID, @@ -565,7 +565,7 @@ describe('getSecondaryAction', () => { const personalDetails = { [EMPLOYEE_ACCOUNT_ID]: {login: EMPLOYEE_EMAIL}, [ADMIN_ACCOUNT_ID]: {login: ADMIN_EMAIL}, - } + }; const policy = { id: POLICY_ID, From d74fe8bf3724e9aefde651022594750d1718ead1 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 30 Apr 2025 23:28:05 +0200 Subject: [PATCH 16/19] fix type --- src/pages/ReportChangeWorkspacePage.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pages/ReportChangeWorkspacePage.tsx b/src/pages/ReportChangeWorkspacePage.tsx index 3b5137b74cd4f..b0b0707b995f8 100644 --- a/src/pages/ReportChangeWorkspacePage.tsx +++ b/src/pages/ReportChangeWorkspacePage.tsx @@ -35,8 +35,7 @@ function ReportChangeWorkspacePage({report}: ReportChangeWorkspacePageProps) { const {translate} = useLocalize(); const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY); - const oldPolicy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`]; - const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); + const [session] = useOnyx(ONYXKEYS.SESSION); const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); const shouldShowLoadingIndicator = isLoadingApp && !isOffline; @@ -48,22 +47,22 @@ function ReportChangeWorkspacePage({report}: ReportChangeWorkspacePageProps) { Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(reportID)); if (isIOUReport(reportID) && isPolicyAdmin(getPolicy(policyID)) && report.ownerAccountID && !isPolicyMember(getLoginByAccountID(report.ownerAccountID), policyID)) { moveIOUReportToPolicyAndInviteSubmitter(reportID, policyID); - } else if (isIOUReport(reportID) && isPolicyMember(currentUserLogin, policyID)) { + } else if (isIOUReport(reportID) && isPolicyMember(session?.email, policyID)) { moveIOUReportToPolicy(reportID, policyID); } else { changeReportPolicy(reportID, policyID); } }, - [currentUserLogin, report, reportID], + [session?.email, report, reportID], ); const {sections, shouldShowNoResultsFoundMessage, shouldShowSearchInput} = useWorkspaceList({ policies, - currentUserLogin, + currentUserLogin: session?.email, shouldShowPendingDeletePolicy: false, selectedPolicyID: report.policyID, searchTerm: debouncedSearchTerm, - additionalFilter: (newPolicy) => isWorkspaceEligibleForReportChange(newPolicy, report, oldPolicy, currentUserLogin), + additionalFilter: (newPolicy) => isWorkspaceEligibleForReportChange(newPolicy, report, session), }); if (!isMoneyRequestReport(report) || isMoneyRequestReportPendingDeletion(report) || (!report.total && !report.unheldTotal)) { From 62bbf48bfc399ac9f5ae55667333a4a4bcec4b32 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 30 Apr 2025 23:45:47 +0200 Subject: [PATCH 17/19] fix eslint --- src/pages/ReportChangeWorkspacePage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/ReportChangeWorkspacePage.tsx b/src/pages/ReportChangeWorkspacePage.tsx index b0b0707b995f8..5b09e6ad58a5a 100644 --- a/src/pages/ReportChangeWorkspacePage.tsx +++ b/src/pages/ReportChangeWorkspacePage.tsx @@ -34,9 +34,9 @@ function ReportChangeWorkspacePage({report}: ReportChangeWorkspacePageProps) { const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); const {translate} = useLocalize(); - const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY); - const [session] = useOnyx(ONYXKEYS.SESSION); - const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); + const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); + const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false}); + const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: false}); const shouldShowLoadingIndicator = isLoadingApp && !isOffline; const selectPolicy = useCallback( From f884ff0f6a992522e76f85f1370720cb274b4558 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 5 May 2025 21:09:29 +0200 Subject: [PATCH 18/19] fix eslint --- tests/unit/ReportSecondaryActionUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index 020162b0a24d6..cf946c16af949 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -101,7 +101,7 @@ describe('getSecondaryAction', () => { expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.APPROVE)).toBe(true); }); - it('includes APPROVE option for report with RTER violations for all transactions', async () => { + it('includes APPROVE option for report with RTER violations for all transactions', () => { const report = { reportID: REPORT_ID, type: CONST.REPORT.TYPE.EXPENSE, From 11ca3e65efc686d8cf142f6ae25d57e721719933 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 6 May 2025 16:22:18 +0200 Subject: [PATCH 19/19] fix moved message on workspace chat --- src/libs/actions/Report.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9d0f51c8321c7..085080c06131e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -4676,12 +4676,12 @@ function moveIOUReportToPolicy(reportID: string, policyID: string) { const changePolicyReportAction = buildOptimisticChangePolicyReportAction(iouReport.policyID, policyID, true); optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportId}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, value: {[changePolicyReportAction.reportActionID]: changePolicyReportAction}, }); successData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportId}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, value: { [changePolicyReportAction.reportActionID]: { ...changePolicyReportAction, @@ -4691,7 +4691,7 @@ function moveIOUReportToPolicy(reportID: string, policyID: string) { }); failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportId}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, value: {[changePolicyReportAction.reportActionID]: null}, });