11import type { StackScreenProps } from '@react-navigation/stack' ;
22import { hasSeenTourSelector } from '@selectors/Onboarding' ;
33import { validTransactionDraftsSelector } from '@selectors/TransactionDraft' ;
4- import React , { useEffect , useState } from 'react' ;
4+ import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
55import { View } from 'react-native' ;
66import type { OnyxEntry } from 'react-native-onyx' ;
77import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView' ;
@@ -11,6 +11,7 @@ import MoneyRequestConfirmationList from '@components/MoneyRequestConfirmationLi
1111import ScreenWrapper from '@components/ScreenWrapper' ;
1212import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' ;
1313import useLocalize from '@hooks/useLocalize' ;
14+ import useNetwork from '@hooks/useNetwork' ;
1415import useOnyx from '@hooks/useOnyx' ;
1516import usePermissions from '@hooks/usePermissions' ;
1617import usePersonalPolicy from '@hooks/usePersonalPolicy' ;
@@ -19,7 +20,15 @@ import useReportAttributes from '@hooks/useReportAttributes';
1920import useReportIsArchived from '@hooks/useReportIsArchived' ;
2021import useThemeStyles from '@hooks/useThemeStyles' ;
2122import type { GpsPoint } from '@libs/actions/IOU' ;
22- import { getIOURequestPolicyID , getMoneyRequestParticipantsFromReport , initMoneyRequest , updateLastLocationPermissionPrompt } from '@libs/actions/IOU' ;
23+ import {
24+ getIOURequestPolicyID ,
25+ getMoneyRequestParticipantsFromReport ,
26+ initMoneyRequest ,
27+ setMoneyRequestBillable ,
28+ setMoneyRequestReimbursable ,
29+ updateLastLocationPermissionPrompt ,
30+ } from '@libs/actions/IOU' ;
31+ import { setMoneyRequestReceipt } from '@libs/actions/IOU/Receipt' ;
2332import { requestMoney , trackExpense } from '@libs/actions/IOU/TrackExpense' ;
2433import DateUtils from '@libs/DateUtils' ;
2534import { getFileName , readFileAsync } from '@libs/fileDownload/FileUtils' ;
@@ -30,10 +39,11 @@ import navigateAfterInteraction from '@libs/Navigation/navigateAfterInteraction'
3039import Navigation from '@libs/Navigation/Navigation' ;
3140import type { ShareNavigatorParamList } from '@libs/Navigation/types' ;
3241import { getParticipantsOption , getReportOption } from '@libs/OptionsListUtils' ;
33- import { hasOnlyPersonalPolicies as hasOnlyPersonalPoliciesUtil } from '@libs/PolicyUtils' ;
42+ import { hasOnlyPersonalPolicies as hasOnlyPersonalPoliciesUtil , isPaidGroupPolicy } from '@libs/PolicyUtils' ;
3443import { shouldValidateFile } from '@libs/ReceiptUtils' ;
3544import { getReportOrDraftReport , isSelfDM } from '@libs/ReportUtils' ;
3645import { getDefaultTaxCode , getTaxValue } from '@libs/TransactionUtils' ;
46+ import DraftWorkspaceOpener from '@pages/iou/request/step/confirmation/DraftWorkspaceOpener' ;
3747import CONST from '@src/CONST' ;
3848import ONYXKEYS from '@src/ONYXKEYS' ;
3949import type SCREENS from '@src/SCREENS' ;
@@ -107,7 +117,7 @@ function SubmitDetailsPage({
107117 reportID : reportOrAccountID ,
108118 policy,
109119 personalPolicy,
110- currentIouRequestType : CONST . IOU . REQUEST_TYPE . SCAN ,
120+ currentIouRequestType : transaction ?. iouRequestType ,
111121 newIouRequestType : CONST . IOU . REQUEST_TYPE . SCAN ,
112122 report,
113123 parentReport,
@@ -120,13 +130,53 @@ function SubmitDetailsPage({
120130 // eslint-disable-next-line react-hooks/exhaustive-deps
121131 } , [ reportOrAccountID , policy , personalPolicy , report , parentReport , currentDate , currentUserPersonalDetails , hasOnlyPersonalPolicies ] ) ;
122132
133+ // Set receipt on the transaction draft so isScanRequest() returns true and
134+ // compact mode, "Automatic" labels, and receipt image rendering all work correctly
135+ const receiptSource = currentAttachment ?. content ?? fileUri ;
136+ const receiptFileName = getFileName ( currentAttachment ?. content ?? '' ) || fileName ;
137+ const receiptFileType = currentAttachment ?. mimeType ?? fileType ;
138+
139+ useEffect ( ( ) => {
140+ if ( ! receiptSource ) {
141+ return ;
142+ }
143+ setMoneyRequestReceipt ( CONST . IOU . OPTIMISTIC_TRANSACTION_ID , receiptSource , receiptFileName , true , receiptFileType ) ;
144+ } , [ receiptSource , receiptFileName , receiptFileType ] ) ;
145+
123146 const selectedParticipants = unknownUserDetails ? [ unknownUserDetails ] : getMoneyRequestParticipantsFromReport ( report , currentUserPersonalDetails . accountID ) ;
124147 const participants = selectedParticipants . map ( ( participant ) => {
125148 const privateIsArchived = privateIsArchivedMap [ `${ ONYXKEYS . COLLECTION . REPORT_NAME_VALUE_PAIRS } ${ participant . reportID } ` ] ;
126149 return participant ?. accountID
127150 ? getParticipantsOption ( participant , personalDetails )
128151 : getReportOption ( participant , privateIsArchived , policy , personalDetails , conciergeReportID , reportAttributesDerived ) ;
129152 } ) ;
153+
154+ const isPolicyExpenseChat = useMemo ( ( ) => participants ?. some ( ( participant ) => participant . isPolicyExpenseChat ) , [ participants ] ) ;
155+ const policyExpenseChatPolicyID = participants ?. find ( ( participant ) => participant . isPolicyExpenseChat ) ?. policyID ;
156+ const senderPolicyID = participants ?. find ( ( participant ) => ! ! participant && 'isSender' in participant && participant . isSender ) ?. policyID ;
157+ const iouType = isSelfDM ( report ) ? CONST . IOU . TYPE . TRACK : CONST . IOU . TYPE . SUBMIT ;
158+ const { isOffline} = useNetwork ( ) ;
159+ const isCreatingTrackExpense = iouType === CONST . IOU . TYPE . TRACK ;
160+
161+ // Initialize billable/reimbursable from policy defaults (mirrors IOURequestStepConfirmation)
162+ const defaultBillable = ! ! policy ?. defaultBillable ;
163+ useEffect ( ( ) => {
164+ setMoneyRequestBillable ( CONST . IOU . OPTIMISTIC_TRANSACTION_ID , defaultBillable ) ;
165+ } , [ defaultBillable ] ) ;
166+
167+ useEffect ( ( ) => {
168+ const defaultReimbursable = ( isPolicyExpenseChat && isPaidGroupPolicy ( policy ) ) || isCreatingTrackExpense ? ( policy ?. defaultReimbursable ?? true ) : true ;
169+ setMoneyRequestReimbursable ( CONST . IOU . OPTIMISTIC_TRANSACTION_ID , defaultReimbursable ) ;
170+ } , [ policy , isPolicyExpenseChat , isCreatingTrackExpense ] ) ;
171+
172+ const setBillable = useCallback ( ( billable : boolean ) => {
173+ setMoneyRequestBillable ( CONST . IOU . OPTIMISTIC_TRANSACTION_ID , billable ) ;
174+ } , [ ] ) ;
175+
176+ const setReimbursable = useCallback ( ( reimbursable : boolean ) => {
177+ setMoneyRequestReimbursable ( CONST . IOU . OPTIMISTIC_TRANSACTION_ID , reimbursable ) ;
178+ } , [ ] ) ;
179+
130180 const trimmedComment = transaction ?. comment ?. comment ?. trim ( ) ?? '' ;
131181 const transactionAmount = transaction ?. amount ?? 0 ;
132182 const transactionTaxAmount = transaction ?. taxAmount ?? 0 ;
@@ -224,12 +274,7 @@ function SubmitDetailsPage({
224274 }
225275 } ;
226276
227- const onSuccess = ( file : File , locationPermissionGranted ?: boolean ) => {
228- const participant = selectedParticipants . at ( 0 ) ;
229- if ( ! participant ) {
230- return ;
231- }
232-
277+ const onSuccess = ( participant : Participant , file : File , locationPermissionGranted ?: boolean ) => {
233278 const receipt : Receipt = file ;
234279 receipt . state = file && CONST . IOU . RECEIPT_STATE . SCAN_READY ;
235280 if ( locationPermissionGranted ) {
@@ -250,7 +295,7 @@ function SubmitDetailsPage({
250295 finishRequestAndNavigate ( participant , receipt ) ;
251296 } ;
252297
253- const onConfirm = ( gpsRequired ?: boolean ) => {
298+ const onConfirm = ( listOfParticipants ?: Participant [ ] , gpsRequired ?: boolean ) => {
254299 const shouldStartLocationPermissionFlow =
255300 gpsRequired &&
256301 ( ! lastLocationPermissionPrompt ||
@@ -265,43 +310,64 @@ function SubmitDetailsPage({
265310 return ;
266311 }
267312
313+ const participant = listOfParticipants ?. at ( 0 ) ?? selectedParticipants . at ( 0 ) ;
314+ if ( ! participant ) {
315+ return ;
316+ }
317+
268318 readFileAsync (
269- fileUri ,
270- fileName ,
271- ( file ) => onSuccess ( file , shouldStartLocationPermissionFlow ) ,
319+ receiptSource ,
320+ receiptFileName ,
321+ ( file ) => onSuccess ( participant , file , shouldStartLocationPermissionFlow ) ,
272322 ( ) => { } ,
273- fileType ,
323+ receiptFileType ,
274324 ) ;
275325 } ;
276326
277327 return (
278328 < ScreenWrapper testID = "SubmitDetailsPage" >
279329 < FullPageNotFoundView shouldShow = { ! reportOrAccountID } >
330+ < DraftWorkspaceOpener
331+ isCreatingTrackExpense = { isCreatingTrackExpense }
332+ policyID = { policy ?. id }
333+ policyPendingAction = { policy ?. pendingAction }
334+ policyExpenseChatPolicyID = { policyExpenseChatPolicyID }
335+ senderPolicyID = { senderPolicyID }
336+ isOffline = { isOffline }
337+ />
280338 < HeaderWithBackButton
281- title = { translate ( 'common.details ' ) }
339+ title = { translate ( 'iou.confirmDetails ' ) }
282340 onBackButtonPress = { ( ) => Navigation . goBack ( ) }
283341 />
284342 < LocationPermissionModal
285343 startPermissionFlow = { startLocationPermissionFlow }
286344 resetPermissionFlow = { ( ) => setStartLocationPermissionFlow ( false ) }
287- onGrant = { onConfirm }
345+ onGrant = { ( ) => onConfirm ( undefined , true ) }
288346 onDeny = { ( ) => {
289347 updateLastLocationPermissionPrompt ( ) ;
290348 setStartLocationPermissionFlow ( false ) ;
291349 navigateAfterInteraction ( ( ) => {
292- onConfirm ( false ) ;
350+ onConfirm ( undefined , false ) ;
293351 } ) ;
294352 } }
295353 />
296354 < View style = { [ styles . containerWithSpaceBetween , styles . pointerEventsBoxNone ] } >
297355 < MoneyRequestConfirmationList
298356 transaction = { transaction }
299357 selectedParticipants = { participants }
300- onConfirm = { ( ) => onConfirm ( true ) }
301- receiptPath = { fileUri }
302- receiptFilename = { getFileName ( fileName ) }
358+ iouType = { iouType }
359+ onToggleBillable = { setBillable }
360+ onToggleReimbursable = { setReimbursable }
361+ isPolicyExpenseChat = { isPolicyExpenseChat }
362+ policyID = { policy ?. id }
363+ onConfirm = { ( updatedParticipants ) => onConfirm ( updatedParticipants , true ) }
364+ receiptPath = { receiptSource }
365+ receiptFilename = { receiptFileName }
303366 reportID = { reportOrAccountID }
304367 shouldShowSmartScanFields = { false }
368+ shouldDisplayReceipt
369+ isReceiptEditable
370+ action = { CONST . IOU . ACTION . CREATE }
305371 onPDFLoadError = { ( ) => {
306372 if ( errorTitle ) {
307373 return ;
0 commit comments