Skip to content
Merged
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
7 changes: 0 additions & 7 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -860,12 +860,6 @@ function MoneyRequestConfirmationList({
*/
const confirm = useCallback(
(paymentMethod: PaymentMethodType | undefined) => {
if (expensesNumber > 1) {
// TODO: remove early return when bulk expense creation is implemented
// https://github.com/Expensify/App/issues/61184
return;
}

if (!!routeError || !transactionID) {
return;
}
Expand Down Expand Up @@ -934,7 +928,6 @@ function MoneyRequestConfirmationList({
isMerchantEmpty,
shouldDisplayFieldError,
transaction,
expensesNumber,
iouCategory.length,
formError,
iouType,
Expand Down
16 changes: 11 additions & 5 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,7 @@
isPersonalTrackingExpense?: boolean;
existingTransactionThreadReportID?: string;
linkedTrackedExpenseReportAction?: ReportAction;
optimisticCreatedReportActionID?: string;
};

type OptimisticTaskReport = SetRequired<
Expand Down Expand Up @@ -880,7 +881,7 @@
const parsedReportActionMessageCache: Record<string, string> = {};

let conciergeReportID: OnyxEntry<string>;
Onyx.connect({

Check warning on line 884 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.CONCIERGE_REPORT_ID,
callback: (value) => {
conciergeReportID = value;
Expand All @@ -888,7 +889,7 @@
});

const defaultAvatarBuildingIconTestID = 'SvgDefaultAvatarBuilding Icon';
Onyx.connect({

Check warning on line 892 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
// When signed out, val is undefined
Expand All @@ -906,7 +907,7 @@
let allPersonalDetails: OnyxEntry<PersonalDetailsList>;
let allPersonalDetailLogins: string[];
let currentUserPersonalDetails: OnyxEntry<PersonalDetails>;
Onyx.connect({

Check warning on line 910 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
if (currentUserAccountID) {
Expand All @@ -918,14 +919,14 @@
});

let allReportsDraft: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 922 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_DRAFT,
waitForCollectionCallback: true,
callback: (value) => (allReportsDraft = value),
});

let allPolicies: OnyxCollection<Policy>;
Onyx.connect({

Check warning on line 929 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => (allPolicies = value),
Expand All @@ -933,7 +934,7 @@

let allReports: OnyxCollection<Report>;
let reportsByPolicyID: ReportByPolicyMap;
Onyx.connect({

Check warning on line 937 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -970,14 +971,14 @@
});

let allBetas: OnyxEntry<Beta[]>;
Onyx.connect({

Check warning on line 974 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.BETAS,
callback: (value) => (allBetas = value),
});

let allTransactions: OnyxCollection<Transaction> = {};
let reportsTransactions: Record<string, Transaction[]> = {};
Onyx.connect({

Check warning on line 981 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -1003,7 +1004,7 @@
});

let allReportActions: OnyxCollection<ReportActions>;
Onyx.connect({

Check warning on line 1007 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand All @@ -1016,7 +1017,7 @@

let allReportMetadata: OnyxCollection<ReportMetadata>;
const allReportMetadataKeyValue: Record<string, ReportMetadata> = {};
Onyx.connect({

Check warning on line 1020 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_METADATA,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -5796,6 +5797,7 @@
* @param currency - IOU currency.
* @param isSendingMoney - If we pay someone the IOU should be created as settled
* @param parentReportActionID - The parent report action ID of the IOU report
* @param optimisticIOUReportID - Optimistic IOU report id
*/

function buildOptimisticIOUReport(
Expand All @@ -5806,6 +5808,7 @@
currency: string,
isSendingMoney = false,
parentReportActionID?: string,
optimisticIOUReportID?: string,
): OptimisticIOUReport {
const formattedTotal = convertToDisplayString(total, currency);
const personalDetails = getPersonalDetailsForAccountID(payerAccountID);
Expand All @@ -5827,7 +5830,7 @@
managerID: payerAccountID,
ownerAccountID: payeeAccountID,
participants,
reportID: generateReportID(),
reportID: optimisticIOUReportID ?? generateReportID(),
stateNum: isSendingMoney ? CONST.REPORT.STATE_NUM.APPROVED : CONST.REPORT.STATE_NUM.SUBMITTED,
statusNum: isSendingMoney ? CONST.REPORT.STATUS_NUM.REIMBURSED : CONST.REPORT.STATE_NUM.SUBMITTED,
total,
Expand Down Expand Up @@ -5954,6 +5957,7 @@
* @param currency
* @param reimbursable – Whether the expense is reimbursable
* @param parentReportActionID – The parent ReportActionID of the PolicyExpenseChat
* @param optimisticIOUReportID – Optimistic IOU report id
*/
function buildOptimisticExpenseReport(
chatReportID: string | undefined,
Expand All @@ -5963,6 +5967,7 @@
currency: string,
nonReimbursableTotal = 0,
parentReportActionID?: string,
optimisticIOUReportID?: string,
): OptimisticExpenseReport {
// The amount for Expense reports are stored as negative value in the database
const storedTotal = total * -1;
Expand All @@ -5977,7 +5982,7 @@
const {stateNum, statusNum} = getExpenseReportStateAndStatus(policy);

const expenseReport: OptimisticExpenseReport = {
reportID: generateReportID(),
reportID: optimisticIOUReportID ?? generateReportID(),
chatReportID,
policyID,
type: CONST.REPORT.TYPE.EXPENSE,
Expand Down Expand Up @@ -6954,9 +6959,9 @@
* Returns the necessary reportAction onyx data to indicate that the chat has been created optimistically
* @param [created] - Action created time
*/
function buildOptimisticCreatedReportAction(emailCreatingAction: string, created = DateUtils.getDBTime()): OptimisticCreatedReportAction {
function buildOptimisticCreatedReportAction(emailCreatingAction: string, created = DateUtils.getDBTime(), optimisticReportActionID?: string): OptimisticCreatedReportAction {
return {
reportActionID: rand64(),
reportActionID: optimisticReportActionID ?? rand64(),
actionName: CONST.REPORT.ACTIONS.TYPE.CREATED,
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
actorAccountID: currentUserAccountID,
Expand Down Expand Up @@ -7718,8 +7723,9 @@
isPersonalTrackingExpense,
existingTransactionThreadReportID,
linkedTrackedExpenseReportAction,
optimisticCreatedReportActionID,
}: OptimisticMoneyRequestEntities): [OptimisticCreatedReportAction, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport, OptimisticCreatedReportAction | null] {
const createdActionForChat = buildOptimisticCreatedReportAction(payeeEmail);
const createdActionForChat = buildOptimisticCreatedReportAction(payeeEmail, undefined, optimisticCreatedReportActionID);

// The `CREATED` action must be optimistically generated before the IOU action so that it won't appear after the IOU action in the chat.
const iouActionCreationTime = DateUtils.getDBTime();
Expand Down
28 changes: 25 additions & 3 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,10 @@ type RequestMoneyInformation = {
shouldPlaySound?: boolean;
shouldHandleNavigation?: boolean;
backToReport?: string;
optimisticChatReportID?: string;
optimisticCreatedReportActionID?: string;
optimisticIOUReportID?: string;
optimisticReportPreviewActionID?: string;
};

type MoneyRequestInformationParams = {
Expand All @@ -440,6 +444,10 @@ type MoneyRequestInformationParams = {
retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt;
isSplitExpense?: boolean;
testDriveCommentReportActionID?: string;
optimisticChatReportID?: string;
optimisticCreatedReportActionID?: string;
optimisticIOUReportID?: string;
optimisticReportPreviewActionID?: string;
};

type MoneyRequestOptimisticParams = {
Expand Down Expand Up @@ -3231,6 +3239,10 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma
retryParams,
isSplitExpense,
testDriveCommentReportActionID,
optimisticChatReportID,
optimisticCreatedReportActionID,
optimisticIOUReportID,
optimisticReportPreviewActionID,
} = moneyRequestInformation;
const {payeeAccountID = userAccountID, payeeEmail = currentUserEmail, participant} = participantParams;
const {policy, policyCategories, policyTagList} = policyParams;
Expand Down Expand Up @@ -3274,6 +3286,7 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma
isNewChatReport = true;
chatReport = buildOptimisticChatReport({
participantList: [payerAccountID, payeeAccountID],
optimisticReportID: optimisticChatReportID,
});
}

Expand All @@ -3290,8 +3303,8 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma

if (!iouReport || shouldCreateNewMoneyRequestReport) {
iouReport = isPolicyExpenseChat
? buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID, payeeAccountID, amount, currency)
: buildOptimisticIOUReport(payeeAccountID, payerAccountID, amount, chatReport.reportID, currency);
? buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID, payeeAccountID, amount, currency, undefined, undefined, optimisticIOUReportID)
: buildOptimisticIOUReport(payeeAccountID, payerAccountID, amount, chatReport.reportID, currency, undefined, undefined, optimisticIOUReportID);
} else if (isPolicyExpenseChat) {
// Splitting doesn’t affect the amount, so no adjustment is needed
// The amount remains constant after the split
Expand Down Expand Up @@ -3370,6 +3383,7 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma
transactionID: optimisticTransaction.transactionID,
paymentType: isSelectedManagerMcTest(participant.login) || transactionParams.receipt?.isTestDriveReceipt ? CONST.IOU.PAYMENT_TYPE.ELSEWHERE : undefined,
existingTransactionThreadReportID: linkedTrackedExpenseReportAction?.childReportID,
optimisticCreatedReportActionID,
linkedTrackedExpenseReportAction,
});

Expand All @@ -3378,7 +3392,7 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma
if (reportPreviewAction) {
reportPreviewAction = updateReportPreview(iouReport, reportPreviewAction, false, comment, optimisticTransaction);
} else {
reportPreviewAction = buildOptimisticReportPreview(chatReport, iouReport, comment, optimisticTransaction);
reportPreviewAction = buildOptimisticReportPreview(chatReport, iouReport, comment, optimisticTransaction, undefined, optimisticReportPreviewActionID);
chatReport.lastVisibleActionCreated = reportPreviewAction.created;

// Generated ReportPreview action is a parent report action of the iou report.
Expand Down Expand Up @@ -5179,6 +5193,10 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) {
shouldHandleNavigation = true,
backToReport,
shouldPlaySound = true,
optimisticChatReportID,
optimisticCreatedReportActionID,
optimisticIOUReportID,
optimisticReportPreviewActionID,
} = requestMoneyInformation;
const {payeeAccountID} = participantParams;
const parsedComment = getParsedComment(transactionParams.comment ?? '');
Expand Down Expand Up @@ -5257,6 +5275,10 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) {
existingTransaction: isDistanceRequestTransactionUtils(existingTransaction) ? existingTransaction : undefined,
retryParams,
testDriveCommentReportActionID,
optimisticChatReportID,
optimisticCreatedReportActionID,
optimisticIOUReportID,
optimisticReportPreviewActionID,
});
const activeReportID = isMoneyRequestReport ? report?.reportID : chatReport.reportID;

Expand Down
10 changes: 10 additions & 0 deletions src/pages/iou/request/step/IOURequestStepConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {isMovingTransactionFromTrackExpense as isMovingTransactionFromTrackExpen
import Log from '@libs/Log';
import navigateAfterInteraction from '@libs/Navigation/navigateAfterInteraction';
import Navigation from '@libs/Navigation/Navigation';
import {rand64} from '@libs/NumberUtils';
import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils';
import Performance from '@libs/Performance';
import {generateReportID, getBankAccountRoute, getReportOrDraftReport, isProcessingReport, isReportOutstanding, isSelectedManagerMcTest} from '@libs/ReportUtils';
Expand Down Expand Up @@ -483,6 +484,11 @@ function IOURequestStepConfirmation({
return;
}

const optimisticChatReportID = generateReportID();
const optimisticCreatedReportActionID = rand64();
const optimisticIOUReportID = generateReportID();
const optimisticReportPreviewActionID = rand64();

transactions.forEach((item, index) => {
const receipt = receiptFiles[item.transactionID];
const isTestReceipt = receipt?.isTestReceipt ?? false;
Expand All @@ -494,6 +500,10 @@ function IOURequestStepConfirmation({

requestMoneyIOUActions({
report,
optimisticChatReportID,
optimisticCreatedReportActionID,
optimisticIOUReportID,
optimisticReportPreviewActionID,
participantParams: {
payeeEmail: currentUserPersonalDetails.login,
payeeAccountID: currentUserPersonalDetails.accountID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import Text from '@components/Text';
import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import usePolicy from '@hooks/usePolicy';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -102,9 +103,8 @@ function IOURequestStepScan({
const hasFlash = !!device?.hasFlash;
const camera = useRef<Camera>(null);
const [flash, setFlash] = useState(false);
// TODO: use correct canUseMultiScan value when all multi-scan functionality is implemented
// const canUseMultiScan = isBetaEnabled(CONST.BETAS.NEWDOT_MULTI_SCAN) && !isEditing && iouType !== CONST.IOU.TYPE.SPLIT && !backTo && !backToReport;
const canUseMultiScan = false;
const {isBetaEnabled} = usePermissions();
const canUseMultiScan = isBetaEnabled(CONST.BETAS.NEWDOT_MULTI_SCAN) && !isEditing && iouType !== CONST.IOU.TYPE.SPLIT && !backTo && !backToReport;
const [startLocationPermissionFlow, setStartLocationPermissionFlow] = useState(false);
const [receiptFiles, setReceiptFiles] = useState<ReceiptFile[]>([]);
const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true});
Expand Down
4 changes: 1 addition & 3 deletions src/pages/iou/request/step/IOURequestStepScan/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,7 @@ function IOURequestStepScan({
const [dismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {canBeMissing: true});
const [isLoadingReceipt, setIsLoadingReceipt] = useState(false);
const isEditing = action === CONST.IOU.ACTION.EDIT;
// TODO: use correct canUseMultiScan value when all multi-scan functionality is implemented
// const canUseMultiScan = isBetaEnabled(CONST.BETAS.NEWDOT_MULTI_SCAN) && !isEditing && iouType !== CONST.IOU.TYPE.SPLIT && !backTo && !backToReport;
const canUseMultiScan = false;
const canUseMultiScan = isBetaEnabled(CONST.BETAS.NEWDOT_MULTI_SCAN) && !isEditing && iouType !== CONST.IOU.TYPE.SPLIT && !backTo && !backToReport;

@dominictb dominictb Oct 19, 2025

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.

Relying on backToReport here causes the multi-scan feature to be missing when initiating the expense creation flow from empty report preview's Add expense button.

More details: #70706


const [optimisticTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {
selector: (items) => Object.values(items ?? {}),
Expand Down
Loading