From f27a47e3b2a8a28ca0eb7f4bb908df6d809d1b84 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 8 Jul 2025 12:57:59 +0200 Subject: [PATCH 1/6] fix: accept only one file when replacing receipt or adding to manual request --- .../iou/request/step/IOURequestStepScan/index.native.tsx | 6 ++++-- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 804335e8d50a..da3eff3ad331 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -130,6 +130,8 @@ function IOURequestStepScan({ return allTransactions.filter((transaction): transaction is Transaction => !!transaction); }, [initialTransaction, initialTransactionID, optimisticTransactions]); + const shouldAcceptMultipleFiles = isBetaEnabled(CONST.BETAS.NEWDOT_MULTI_FILES_DRAG_AND_DROP) && !isEditing && !backTo; + const blinkOpacity = useSharedValue(0); const blinkStyle = useAnimatedStyle(() => ({ opacity: blinkOpacity.get(), @@ -532,7 +534,7 @@ function IOURequestStepScan({ files.forEach((file, index) => { const transaction = - !isBetaEnabled(CONST.BETAS.NEWDOT_MULTI_FILES_DRAG_AND_DROP) || (index === 0 && transactions.length === 1 && !initialTransaction?.receipt) + !shouldAcceptMultipleFiles || (index === 0 && transactions.length === 1 && !initialTransaction?.receipt) ? (initialTransaction as Partial) : buildOptimisticTransactionAndCreateDraft({ initialTransaction: initialTransaction as Partial, @@ -817,7 +819,7 @@ function IOURequestStepScan({ setIsLoaderVisible(true)} - fileLimit={isBetaEnabled(CONST.BETAS.NEWDOT_MULTI_FILES_DRAG_AND_DROP) ? CONST.API_ATTACHMENT_VALIDATIONS.MAX_FILE_LIMIT : 1} + fileLimit={shouldAcceptMultipleFiles ? CONST.API_ATTACHMENT_VALIDATIONS.MAX_FILE_LIMIT : 1} > {({openPicker}) => ( Object.values(items ?? {}), @@ -573,7 +573,7 @@ function IOURequestStepScan({ files.forEach((file, index) => { const source = URL.createObjectURL(file as Blob); const transaction = - !isBetaEnabled(CONST.BETAS.NEWDOT_MULTI_FILES_DRAG_AND_DROP) || (index === 0 && transactions.length === 1 && !initialTransaction?.receipt) + !shouldAcceptMultipleFiles || (index === 0 && transactions.length === 1 && !initialTransaction?.receipt) ? (initialTransaction as Partial) : buildOptimisticTransactionAndCreateDraft({ initialTransaction: initialTransaction as Partial, From fb4b9939222f220cd5a115403245f4d055a87d1c Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 8 Jul 2025 19:52:16 +0200 Subject: [PATCH 2/6] fix: show receipt when submitting track expense to someone --- src/pages/iou/request/step/IOURequestStepScan/index.native.tsx | 2 +- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index da3eff3ad331..368e3a570330 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -534,7 +534,7 @@ function IOURequestStepScan({ files.forEach((file, index) => { const transaction = - !shouldAcceptMultipleFiles || (index === 0 && transactions.length === 1 && !initialTransaction?.receipt) + !shouldAcceptMultipleFiles || (index === 0 && transactions.length === 1 && !initialTransaction?.receipt?.source) ? (initialTransaction as Partial) : buildOptimisticTransactionAndCreateDraft({ initialTransaction: initialTransaction as Partial, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 666a0319bd9d..33daddc86737 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -573,7 +573,7 @@ function IOURequestStepScan({ files.forEach((file, index) => { const source = URL.createObjectURL(file as Blob); const transaction = - !shouldAcceptMultipleFiles || (index === 0 && transactions.length === 1 && !initialTransaction?.receipt) + !shouldAcceptMultipleFiles || (index === 0 && transactions.length === 1 && !initialTransaction?.receipt?.source) ? (initialTransaction as Partial) : buildOptimisticTransactionAndCreateDraft({ initialTransaction: initialTransaction as Partial, From f975f5cbce51d58b337c31fb08b35ea651689cd9 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 8 Jul 2025 20:09:31 +0200 Subject: [PATCH 3/6] fix: do not change participant to active policy when participants already chosen --- .../request/step/IOURequestStepScan/index.native.tsx | 12 ++++++++++-- .../iou/request/step/IOURequestStepScan/index.tsx | 10 +++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 368e3a570330..3bdc6927bb1c 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -442,6 +442,12 @@ function IOURequestStepScan({ return; } + // If the initial transaction already has the participants selected, then we can skip the participants step and go straight to the confirmation step. + if (initialTransaction?.participants && initialTransaction?.participants.length > 0) { + navigateToConfirmationPage(false, initialTransaction?.reportID); + return; + } + // If there was no reportID, then that means the user started this flow from the global + menu // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { @@ -466,16 +472,18 @@ function IOURequestStepScan({ report, reportNameValuePairs, iouType, + initialTransaction?.participants, + initialTransaction?.currency, + initialTransaction?.reportID, activePolicy, initialTransactionID, navigateToConfirmationPage, shouldSkipConfirmation, personalDetails, createTransaction, - currentUserPersonalDetails.login, + currentUserPersonalDetails?.login, currentUserPersonalDetails.accountID, reportID, - initialTransaction?.currency, transactionTaxCode, transactionTaxAmount, policy, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 33daddc86737..0709f02c040a 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -503,6 +503,12 @@ function IOURequestStepScan({ return; } + // If the initial transaction already has the participants selected, then we can skip the participants step and go straight to the confirmation step. + if (initialTransaction?.participants && initialTransaction?.participants.length > 0) { + navigateToConfirmationPage(false, initialTransaction?.reportID); + return; + } + // If there was no reportID, then that means the user started this flow from the global + menu // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { @@ -527,6 +533,9 @@ function IOURequestStepScan({ report, reportNameValuePairs, iouType, + initialTransaction?.participants, + initialTransaction?.currency, + initialTransaction?.reportID, activePolicy, initialTransactionID, navigateToConfirmationPage, @@ -536,7 +545,6 @@ function IOURequestStepScan({ currentUserPersonalDetails?.login, currentUserPersonalDetails.accountID, reportID, - initialTransaction?.currency, transactionTaxCode, transactionTaxAmount, policy, From 52df08ef2fad1f948f2c0da073cdd1eee367f9dd Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 8 Jul 2025 21:17:44 +0200 Subject: [PATCH 4/6] fix: participants --- src/pages/iou/request/step/IOURequestStepScan/index.native.tsx | 3 ++- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 3bdc6927bb1c..7633de0962be 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -444,7 +444,8 @@ function IOURequestStepScan({ // If the initial transaction already has the participants selected, then we can skip the participants step and go straight to the confirmation step. if (initialTransaction?.participants && initialTransaction?.participants.length > 0) { - navigateToConfirmationPage(false, initialTransaction?.reportID); + const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipants(receiptFile.transactionID, initialTransaction?.participants)); + Promise.all(setParticipantsPromises).then(() => navigateToConfirmationPage(false, initialTransaction?.reportID)); return; } diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 0709f02c040a..818f328495a1 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -505,7 +505,8 @@ function IOURequestStepScan({ // If the initial transaction already has the participants selected, then we can skip the participants step and go straight to the confirmation step. if (initialTransaction?.participants && initialTransaction?.participants.length > 0) { - navigateToConfirmationPage(false, initialTransaction?.reportID); + const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipants(receiptFile.transactionID, initialTransaction?.participants)); + Promise.all(setParticipantsPromises).then(() => navigateToConfirmationPage(false, initialTransaction?.reportID)); return; } From eb5bb7db69ca611b8aa74d4aef0bfae814dfbb47 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 9 Jul 2025 11:49:20 +0200 Subject: [PATCH 5/6] fix: restore filetype validation --- src/components/AttachmentComposerModal.tsx | 2 +- src/hooks/useFilesValidation.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AttachmentComposerModal.tsx b/src/components/AttachmentComposerModal.tsx index a317716f74cf..dace0b604c94 100644 --- a/src/components/AttachmentComposerModal.tsx +++ b/src/components/AttachmentComposerModal.tsx @@ -175,7 +175,7 @@ function AttachmentComposerModal({onConfirm, onModalShow = () => {}, onModalHide } }, [fileError, validFilesToUpload, convertFileToAttachment, getModalType]); - const {ErrorModal, validateFiles, PDFValidationComponent} = useFilesValidation(setValidFilesToUpload); + const {ErrorModal, validateFiles, PDFValidationComponent} = useFilesValidation(setValidFilesToUpload, false); const confirmAndContinue = () => { if (fileError === CONST.FILE_VALIDATION_ERRORS.MAX_FILE_LIMIT_EXCEEDED) { diff --git a/src/hooks/useFilesValidation.tsx b/src/hooks/useFilesValidation.tsx index 6e09bfacc389..a3ae468f4b49 100644 --- a/src/hooks/useFilesValidation.tsx +++ b/src/hooks/useFilesValidation.tsx @@ -27,7 +27,7 @@ type ErrorObject = { fileExtension?: string; }; -function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => void, isValidatingReceipts = false) { +function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => void, isValidatingReceipts = true) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [isErrorModalVisible, setIsErrorModalVisible] = useState(false); From 08c4a43205b6cc312caa1a7e466ab7a73007a808 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 9 Jul 2025 12:57:01 +0200 Subject: [PATCH 6/6] fix: fix problem with active policy --- .../step/IOURequestStepScan/index.native.tsx | 23 +++++++++------- .../request/step/IOURequestStepScan/index.tsx | 27 +++++++++++-------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 7633de0962be..9c3f234e1d96 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -442,17 +442,16 @@ function IOURequestStepScan({ return; } - // If the initial transaction already has the participants selected, then we can skip the participants step and go straight to the confirmation step. - if (initialTransaction?.participants && initialTransaction?.participants.length > 0) { - const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipants(receiptFile.transactionID, initialTransaction?.participants)); - Promise.all(setParticipantsPromises).then(() => navigateToConfirmationPage(false, initialTransaction?.reportID)); - return; - } - // If there was no reportID, then that means the user started this flow from the global + menu // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. - if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { - const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + if ( + (!initialTransaction?.participants || initialTransaction?.participants?.at(0)?.reportID === activePolicyExpenseChat?.reportID) && + iouType === CONST.IOU.TYPE.CREATE && + isPaidGroupPolicy(activePolicy) && + activePolicy?.isPolicyExpenseChatEnabled && + !shouldRestrictUserBillableActions(activePolicy.id) + ) { const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat)); Promise.all(setParticipantsPromises).then(() => Navigation.navigate( @@ -465,6 +464,12 @@ function IOURequestStepScan({ ), ); } else { + // If the initial transaction already has the participants selected, then we can skip the participants step and go straight to the confirmation step. + if (initialTransaction?.participants && initialTransaction?.participants.length > 0) { + const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipants(receiptFile.transactionID, initialTransaction?.participants)); + Promise.all(setParticipantsPromises).then(() => navigateToConfirmationPage(false, initialTransaction?.reportID)); + return; + } navigateToParticipantPage(iouType, initialTransactionID, reportID); } }, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 818f328495a1..0a1e1fc46fdd 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -317,7 +317,7 @@ function IOURequestStepScan({ }, [backTo]); const navigateToConfirmationPage = useCallback( - (isTestTransaction = false, reportIDParam: string | undefined = undefined) => { + (shouldNavigateToSubmit = false, reportIDParam: string | undefined = undefined) => { switch (iouType) { case CONST.IOU.TYPE.REQUEST: Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.SUBMIT, initialTransactionID, reportID, backToReport)); @@ -329,7 +329,7 @@ function IOURequestStepScan({ Navigation.navigate( ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( CONST.IOU.ACTION.CREATE, - isTestTransaction ? CONST.IOU.TYPE.SUBMIT : iouType, + shouldNavigateToSubmit ? CONST.IOU.TYPE.SUBMIT : iouType, initialTransactionID, // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing reportIDParam || reportID, @@ -503,17 +503,16 @@ function IOURequestStepScan({ return; } - // If the initial transaction already has the participants selected, then we can skip the participants step and go straight to the confirmation step. - if (initialTransaction?.participants && initialTransaction?.participants.length > 0) { - const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipants(receiptFile.transactionID, initialTransaction?.participants)); - Promise.all(setParticipantsPromises).then(() => navigateToConfirmationPage(false, initialTransaction?.reportID)); - return; - } - // If there was no reportID, then that means the user started this flow from the global + menu // and an optimistic reportID was generated. In that case, the next step is to select the participants for this expense. - if (iouType === CONST.IOU.TYPE.CREATE && isPaidGroupPolicy(activePolicy) && activePolicy?.isPolicyExpenseChatEnabled && !shouldRestrictUserBillableActions(activePolicy.id)) { - const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + const activePolicyExpenseChat = getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id); + if ( + (!initialTransaction?.participants || initialTransaction?.participants?.at(0)?.reportID === activePolicyExpenseChat?.reportID) && + iouType === CONST.IOU.TYPE.CREATE && + isPaidGroupPolicy(activePolicy) && + activePolicy?.isPolicyExpenseChatEnabled && + !shouldRestrictUserBillableActions(activePolicy.id) + ) { const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipantsFromReport(receiptFile.transactionID, activePolicyExpenseChat)); Promise.all(setParticipantsPromises).then(() => Navigation.navigate( @@ -526,6 +525,12 @@ function IOURequestStepScan({ ), ); } else { + // If the initial transaction already has the participants selected, then we can skip the participants step and go straight to the confirmation step. + if (initialTransaction?.participants && initialTransaction?.participants.length > 0) { + const setParticipantsPromises = files.map((receiptFile) => setMoneyRequestParticipants(receiptFile.transactionID, initialTransaction?.participants)); + Promise.all(setParticipantsPromises).then(() => navigateToConfirmationPage(true, initialTransaction?.reportID)); + return; + } navigateToParticipantPage(iouType, initialTransactionID, reportID); } },