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
16 changes: 16 additions & 0 deletions src/pages/Search/SearchTransactionsChangeReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,22 @@ function SearchTransactionsChangeReport() {
});

const createReport = () => {
if (!policyForMovingExpensesID && !shouldSelectPolicy && selectedTransactionsKeys.length > 0) {
const firstTransactionID = selectedTransactionsKeys.at(0);
if (firstTransactionID) {
Navigation.navigate(
ROUTES.MONEY_REQUEST_UPGRADE.getRoute({
action: CONST.IOU.ACTION.EDIT,
iouType: CONST.IOU.TYPE.SUBMIT,
transactionID: firstTransactionID,
reportID: selectedTransactions[firstTransactionID]?.reportID ?? CONST.REPORT.UNREPORTED_REPORT_ID,
upgradePath: CONST.UPGRADE_PATHS.REPORTS,
}),
);
}
return;
}

if (shouldSelectPolicy) {
Navigation.navigate(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute(true));
return;
Expand Down
108 changes: 101 additions & 7 deletions src/pages/iou/request/step/IOURequestStepUpgrade.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
import {hasSeenTourSelector} from '@selectors/Onboarding';
import React, {useRef, useState} from 'react';
import React, {useCallback, useMemo, useRef, useState} from 'react';
import type {OnyxCollection} from 'react-native-onyx';
import ConfirmModal from '@components/ConfirmModal';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {usePersonalDetails} from '@components/OnyxListItemProvider';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import {useSearchActionsContext, useSearchStateContext} from '@components/Search/SearchContext';
import WorkspaceConfirmationForm from '@components/WorkspaceConfirmationForm';
import type {WorkspaceConfirmationSubmitFunctionParams} from '@components/WorkspaceConfirmationForm';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
import usePreferredPolicy from '@hooks/usePreferredPolicy';
import useThemeStyles from '@hooks/useThemeStyles';
import {setTransactionReport} from '@libs/actions/Transaction';
import {createNewReport} from '@libs/actions/Report';
import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction';
import type CreateWorkspaceParams from '@libs/API/parameters/CreateWorkspaceParams';
import getPlatform from '@libs/getPlatform';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {MoneyRequestNavigatorParamList} from '@libs/Navigation/types';
import {getParticipantsOption} from '@libs/OptionsListUtils';
import {getPersonalDetailsForAccountID, hasViolations as hasViolationsReportUtils} from '@libs/ReportUtils';
import UpgradeConfirmation from '@pages/workspace/upgrade/UpgradeConfirmation';
import UpgradeIntro from '@pages/workspace/upgrade/UpgradeIntro';
import {setCustomUnitRateID, setMoneyRequestParticipants} from '@userActions/IOU';
Expand All @@ -29,6 +34,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {PersonalDetails, Transaction} from '@src/types/onyx';

type IOURequestStepUpgradeProps = PlatformStackScreenProps<MoneyRequestNavigatorParamList, typeof SCREENS.MONEY_REQUEST.STEP_UPGRADE>;

Expand All @@ -39,12 +45,13 @@ function IOURequestStepUpgrade({
}: IOURequestStepUpgradeProps) {
const styles = useThemeStyles();

const {translate} = useLocalize();
const {translate, toLocaleDigit} = useLocalize();
const {isOffline} = useNetwork();
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const personalDetails = usePersonalDetails();

const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`);
const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
const [onboardingPurposeSelected] = useOnyx(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED);

const [isUpgraded, setIsUpgraded] = useState(false);
Expand All @@ -62,6 +69,43 @@ function IOURequestStepUpgrade({
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});

// Hooks for bulk move functionality
const {selectedTransactions} = useSearchStateContext();
const {clearSelectedTransactions} = useSearchActionsContext();
const selectedTransactionsKeys = useMemo(() => Object.keys(selectedTransactions), [selectedTransactions]);
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
const [allPolicyCategories] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CATEGORIES);
const [allReportNextSteps] = useOnyx(ONYXKEYS.COLLECTION.NEXT_STEP);
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [session] = useOnyx(ONYXKEYS.SESSION);
const [betas] = useOnyx(ONYXKEYS.BETAS);

// Build transactions map from selectedTransactions (search results) instead of Onyx TRANSACTION collection
// This ensures that transactions selected from search are properly included in the map passed to changeTransactionsReport
const allTransactions = useMemo(
() =>
Object.values(selectedTransactions).reduce(
(transactionsCollection, transactionItem) => {
if (transactionItem.transaction) {
// eslint-disable-next-line no-param-reassign
transactionsCollection[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transaction.transactionID}`] = transactionItem.transaction;
}
return transactionsCollection;
},
{} as NonNullable<OnyxCollection<Transaction>>,
),
[selectedTransactions],
);

const {isBetaEnabled} = usePermissions();
const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT);
const hasViolations = hasViolationsReportUtils(undefined, transactionViolations, session?.accountID ?? CONST.DEFAULT_NUMBER_ID, session?.email ?? '');

const ownerPersonalDetails = useMemo(
() => getPersonalDetailsForAccountID(selectedReport?.ownerAccountID, personalDetails) as PersonalDetails,
[personalDetails, selectedReport?.ownerAccountID],
);

const feature = Object.values(CONST.UPGRADE_FEATURE_INTRO_MAPPING)
.filter((value) => value.id !== CONST.UPGRADE_FEATURE_INTRO_MAPPING.policyPreventMemberChangingTitle.id)
.find((f) => f.alias === upgradePath);
Expand All @@ -74,9 +118,39 @@ function IOURequestStepUpgrade({
}
};

const afterUpgradeAcknowledged = () => {
const afterUpgradeAcknowledged = useCallback(() => {
const expenseReportID = policyDataRef.current?.expenseChatReportID ?? reportID;
const policyID = policyDataRef.current?.policyID;

// Bulk move expenses
if (upgradePath === CONST.UPGRADE_PATHS.REPORTS && policyID && selectedTransactionsKeys.includes(transactionID)) {
const newPolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];

const optimisticReport = createNewReport(ownerPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, newPolicy, betas);

const reportNextStep = allReportNextSteps?.[`${ONYXKEYS.COLLECTION.NEXT_STEP}${optimisticReport.reportID}`];

// Move ALL selected transactions to the new report
changeTransactionsReport({
transactionIDs: selectedTransactionsKeys,
isASAPSubmitBetaEnabled,
accountID: session?.accountID ?? CONST.DEFAULT_NUMBER_ID,
email: session?.email ?? '',
newReport: optimisticReport,
policy: newPolicy,
reportNextStep,
policyCategories: allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`],
allTransactions,
translate,
toLocaleDigit,
});
Comment on lines +140 to +146
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass selected transactions data to bulk move

In this upgrade flow you pass allTransactions from the Onyx TRANSACTION collection into changeTransactionsReport, but the selected items here come from SearchContext (snapshot search results) and aren’t guaranteed to exist in that Onyx collection. changeTransactionsReport filters missing entries and returns early when none are found, so the post-upgrade bulk move can become a no-op for search-based selections. Consider building the allTransactions map from selectedTransactions (as the search change-report flow does) or otherwise ensuring the selected IDs are present before calling.

Useful? React with 👍 / 👎.


clearSelectedTransactions();

Navigation.dismissModal();
return;
}

if (shouldSubmitExpense) {
setMoneyRequestParticipants(transactionID, [
{
Expand Down Expand Up @@ -129,7 +203,29 @@ function IOURequestStepUpgrade({
default:
Navigation.goBack();
}
};
}, [
action,
backTo,
navigateWithMicrotask,
reportID,
shouldSubmitExpense,
transactionID,
upgradePath,
selectedTransactionsKeys,
clearSelectedTransactions,
hasViolations,
isASAPSubmitBetaEnabled,
allPolicies,
allReportNextSteps,
allPolicyCategories,
session?.accountID,
session?.email,
ownerPersonalDetails,
allTransactions,
betas,
translate,
toLocaleDigit,
]);

const participant = transaction?.participants?.[0];
const adminParticipant = isDistanceRateUpgrade && participant?.accountID ? getParticipantsOption(participant, personalDetails) : undefined;
Expand Down Expand Up @@ -170,8 +266,6 @@ function IOURequestStepUpgrade({
policyDataRef.current = policyData;
};

const [session] = useOnyx(ONYXKEYS.SESSION);

const handleConfirmUpgradeWarning = () => {
setIsUpgradeWarningModalOpen(false);
};
Expand Down
4 changes: 2 additions & 2 deletions src/pages/workspace/upgrade/UpgradeConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function UpgradeConfirmation({policyName, afterUpgradeAcknowledged, isReporting,
}, [updateSubscriptionLink]);

const description = useMemo(() => {
if (isCategorizing ?? isReporting) {
if (isCategorizing || isReporting) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CONSISTENCY-2

The ?? operator was likely intentional for null/undefined checking, but || treats falsy values differently (including false, 0, ""). If isCategorizing or isReporting are explicitly false, this will incorrectly fall through to the condition.

Suggested fix: Keep the nullish coalescing operator if the intent was to only check for null/undefined:

if (isCategorizing ?? isReporting) {

Or if you intended OR logic, use explicit checks:

if (isCategorizing || isReporting) {

Note: This appears to be a logical change rather than just a style fix - ensure the new behavior is correct.


Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

return <Text style={[styles.textAlignCenter, styles.w100]}>{translate('workspace.upgrade.completed.categorizeMessage')}</Text>;
}

Expand All @@ -56,7 +56,7 @@ function UpgradeConfirmation({policyName, afterUpgradeAcknowledged, isReporting,
}, [isDistanceRateUpgrade, isCategorizing, isReporting, isTravelUpgrade, policyName, styles.renderHTML, styles.textAlignCenter, styles.w100, translate, subscriptionLink]);

const heading = useMemo(() => {
if (isCategorizing ?? isReporting) {
if (isCategorizing || isReporting) {
return translate('workspace.upgrade.completed.createdWorkspace');
}
return translate('workspace.upgrade.completed.headline');
Expand Down
Loading