Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions src/pages/DynamicReportChangeApproverPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {useDelegateNoAccessState} from '@components/DelegateNoAccessModalProvider';
import FormHelpMessage from '@components/FormHelpMessage';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MoneyReportHeaderModals from '@components/MoneyReportHeaderModals';
import useConfirmApproval from '@components/MoneyReportHeaderPrimaryAction/useConfirmApproval';
import RenderHTML from '@components/RenderHTML';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
Expand Down Expand Up @@ -55,13 +58,17 @@ function DynamicReportChangeApproverPage({report, policy, isLoadingReportData}:
const [selectedApproverType, setSelectedApproverType] = useState<ApproverType>();
const [hasError, setHasError] = useState(false);
const {isBetaEnabled} = usePermissions();
const {isDelegateAccessRestricted} = useDelegateNoAccessState();
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT);
const hasViolations = hasViolationsReportUtils(report?.reportID, transactionViolations, currentUserDetails.accountID, currentUserDetails.login ?? '');
const [isTrackIntentUser] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {selector: isTrackIntentUserSelector});
const hasAutoAppliedRef = useRef(false);
const hasNavigatedToAddApproverRef = useRef(false);
const backPath = useDynamicBackPath(DYNAMIC_ROUTES.REPORT_CHANGE_APPROVER.path);
const isCurrentUserManager = report.managerID === currentUserDetails.accountID;
// The approved animation is part of the report header, which isn't mounted in this RHP, so there is nothing to animate here
const confirmApproval = useConfirmApproval(report.reportID, () => {});

const goBack = () => {
Navigation.goBack(backPath);
Expand All @@ -88,8 +95,26 @@ function DynamicReportChangeApproverPage({report, policy, isLoadingReportData}:
return;
}
assignReportToMe(report, currentUserDetails.accountID, currentUserDetails.email ?? '', policy, hasViolations, isASAPSubmitBetaEnabled, isTrackIntentUser, formatPhoneNumber);
Comment thread
hungvu193 marked this conversation as resolved.
// Taking control only makes the current user the final approver. When they already are the manager, the report
// stays waiting on them, so approve it as well to actually bypass the remaining approvers.
if (isCurrentUserManager && !isDelegateAccessRestricted) {
confirmApproval();
}
Navigation.dismissToPreviousRHP();
}, [selectedApproverType, report, currentUserDetails.accountID, currentUserDetails.email, policy, hasViolations, isASAPSubmitBetaEnabled, isTrackIntentUser, formatPhoneNumber]);
}, [
selectedApproverType,
report,
currentUserDetails.accountID,
currentUserDetails.email,
policy,
hasViolations,
isASAPSubmitBetaEnabled,
isTrackIntentUser,
formatPhoneNumber,
isCurrentUserManager,
confirmApproval,
isDelegateAccessRestricted,
]);

const approverTypes = useMemo(() => {
const data: Array<ListItem<ApproverType>> = [
Expand All @@ -101,8 +126,7 @@ function DynamicReportChangeApproverPage({report, policy, isLoadingReportData}:
},
];

const isCurrentUserManager = report.managerID === currentUserDetails.accountID;
if (!isCurrentUserManager && isAllowedToApproveExpenseReport(report, currentUserDetails.accountID, policy)) {
if (isAllowedToApproveExpenseReport(report, currentUserDetails.accountID, policy)) {
data.push({
text: translate('iou.changeApprover.actions.bypassApprovers'),
keyForList: APPROVER_TYPE.BYPASS_APPROVER,
Expand Down Expand Up @@ -188,6 +212,15 @@ function DynamicReportChangeApproverPage({report, policy, isLoadingReportData}:
);
}

export default withReportOrNotFound()(DynamicReportChangeApproverPage);
// The page reads the hold menu from the MoneyReportHeaderModals context, so the provider has to sit above it
function DynamicReportChangeApproverPageWithModals(props: DynamicReportChangeApproverPageProps) {
return (
<MoneyReportHeaderModals reportID={props.report.reportID}>
<DynamicReportChangeApproverPage {...props} />
</MoneyReportHeaderModals>
);
}

export default withReportOrNotFound()(DynamicReportChangeApproverPageWithModals);
export {APPROVER_TYPE};
export type {ApproverType};
60 changes: 43 additions & 17 deletions src/pages/Search/SearchChangeApproverPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
import {useDelegateNoAccessState} from '@components/DelegateNoAccessModalProvider';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import RenderHTML from '@components/RenderHTML';
Expand All @@ -9,17 +10,20 @@ import SingleSelectListItem from '@components/SelectionList/ListItem/SingleSelec
import type {ListItem} from '@components/SelectionList/types';
import Text from '@components/Text';

import {useCurrencyListActions} from '@hooks/useCurrencyList';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useDelegateAccountID from '@hooks/useDelegateAccountID';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
import useThemeStyles from '@hooks/useThemeStyles';

import {assignReportToMe} from '@libs/actions/IOU/ReportWorkflow';
import {approveMoneyRequest, assignReportToMe} from '@libs/actions/IOU/ReportWorkflow';
import {openBulkChangeApproverPage} from '@libs/actions/Search';
import Navigation from '@libs/Navigation/Navigation';
import {getLoginByAccountID} from '@libs/PersonalDetailsUtils';
import {isControlPolicy, isPolicyAdmin} from '@libs/PolicyUtils';
import {hasViolations as hasViolationsReportUtils, isAllowedToApproveExpenseReport} from '@libs/ReportUtils';

Expand All @@ -33,6 +37,7 @@ import type {Policy, Report} from '@src/types/onyx';

import type {OnyxCollection} from 'react-native-onyx';

import {delegateEmailSelector} from '@selectors/Account';
import {isTrackIntentUserSelector} from '@selectors/Onboarding';
import React, {useEffect, useLayoutEffect, useRef, useState} from 'react';
import {View} from 'react-native';
Expand Down Expand Up @@ -87,6 +92,15 @@ function SearchChangeApproverPage() {
const [hasLoadedApp] = useOnyx(ONYXKEYS.HAS_LOADED_APP);
const [isLoadingBulkChangeApproverPage = true] = useOnyx(ONYXKEYS.IS_LOADING_BULK_CHANGE_APPROVER_PAGE);
const {isOffline} = useNetwork();
const {getCurrencyDecimals} = useCurrencyListActions();
const {isDelegateAccessRestricted} = useDelegateNoAccessState();
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END);
const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED);
const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END);
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
Comment thread
hungvu193 marked this conversation as resolved.
const [delegateEmail] = useOnyx(ONYXKEYS.ACCOUNT, {selector: delegateEmailSelector});
const delegateAccountID = useDelegateAccountID();

const getOnyxReports = (allReports: OnyxCollection<Report>) => {
const reports = Object.create(null) as Record<string, Report>;
Expand Down Expand Up @@ -171,10 +185,34 @@ function SearchChangeApproverPage() {
continue;
}

if (report.managerID !== currentUserDetails.accountID) {
const hasViolations = hasViolationsReportUtils(report.reportID, transactionViolations, currentUserDetails.accountID, currentUserDetails.email ?? '');
assignReportToMe(report, currentUserDetails.accountID, currentUserDetails.email ?? '', policy, hasViolations, isASAPSubmitBetaEnabled, isTrackIntentUser, formatPhoneNumber);
const hasViolations = hasViolationsReportUtils(report.reportID, transactionViolations, currentUserDetails.accountID, currentUserDetails.email ?? '');
assignReportToMe(report, currentUserDetails.accountID, currentUserDetails.email ?? '', policy, hasViolations, isASAPSubmitBetaEnabled, isTrackIntentUser, formatPhoneNumber);
Comment thread
hungvu193 marked this conversation as resolved.

// Taking control only makes the current user the final approver. When they already are the manager, the
// report stays waiting on them, so approve it as well to actually bypass the remaining approvers.
if (report.managerID !== currentUserDetails.accountID || isDelegateAccessRestricted) {
continue;
}

approveMoneyRequest({
Comment thread
hungvu193 marked this conversation as resolved.
getCurrencyDecimals,
expenseReport: report,
expenseReportPolicy: policy,
currentUserAccountIDParam: currentUserDetails.accountID,
currentUserEmailParam: currentUserDetails.email ?? '',
hasViolations,
isASAPSubmitBetaEnabled,
betas,
userBillingGracePeriodEnds,
amountOwed,
ownerBillingGracePeriodEnd,
ownerLogin: getLoginByAccountID(report.ownerAccountID, personalDetails),
delegateEmail,
delegateAccountID,
full: true,
shouldPlaySuccessSound: false,
isTrackIntentUser,
});
}

// Note: This clears both reports and transactions
Expand Down Expand Up @@ -202,19 +240,7 @@ function SearchChangeApproverPage() {
return isPolicyAdmin(policy) && isAllowedToApproveExpenseReport(report, currentUserDetails.accountID, policy);
});

const shouldShowBypassApproversOption =
hasPermission &&
selectedReports.some((selectedReport) => {
const report = selectedReport.reportID ? onyxReports?.[selectedReport.reportID] : undefined;

if (!report) {
return false;
}

return report.managerID !== currentUserDetails.accountID;
});

if (shouldShowBypassApproversOption) {
if (hasPermission) {
data.push({
text: translate('iou.changeApprover.actions.bypassApprovers'),
keyForList: APPROVER_TYPE.BYPASS_APPROVER,
Expand Down
Loading