diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 0def079ce2af..1065b525f83a 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -110,6 +110,7 @@ export default { MONEY_REQUEST_TAG: { route: ':iouType/new/tag/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/tag/${reportID}`}, MONEY_REQUEST_MERCHANT: { route: ':iouType/new/merchant/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/merchant/${reportID}`}, MONEY_REQUEST_WAYPOINT: { route: ':iouType/new/waypoint/:waypointIndex', getRoute: (iouType: string, waypointIndex: number) => `${iouType}/new/waypoint/${waypointIndex}`}, + MONEY_REQUEST_RECEIPT: { route: ':iouType/new/receipt/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/receipt/${reportID}`}, MONEY_REQUEST_ADDRESS: { route: ':iouType/new/address/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/address/${reportID}`}, MONEY_REQUEST_DISTANCE_TAB: { route: ':iouType/new/:reportID?/distance', getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}/distance`}, MONEY_REQUEST_MANUAL_TAB: ':iouType/new/:reportID?/manual', diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index 7f1e58912128..9db9c87c4eb1 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -11,6 +11,7 @@ import participantPropTypes from './participantPropTypes'; import styles from '../styles/styles'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; +import CONST from '../CONST'; import ONYXKEYS from '../ONYXKEYS'; import * as IOU from '../libs/actions/IOU'; import ConfirmModal from './ConfirmModal'; @@ -85,6 +86,15 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, shouldShowPinButton={false} shouldShowThreeDotsButton={isActionOwner && !isSettled} threeDotsMenuItems={[ + ...(TransactionUtils.hasReceipt(transaction) + ? [] + : [ + { + icon: Expensicons.Receipt, + text: translate('receipt.addReceipt'), + onSelected: () => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT)), + }, + ]), { icon: Expensicons.Trashcan, text: translate('reportActionContextMenu.deleteAction', {action: parentReportAction}), diff --git a/src/languages/en.ts b/src/languages/en.ts index 6cd21b1e3da8..def4b351e112 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -489,6 +489,7 @@ export default { flash: 'flash', shutter: 'shutter', gallery: 'gallery', + addReceipt: 'Add receipt', }, iou: { amount: 'Amount', diff --git a/src/languages/es.ts b/src/languages/es.ts index f2065ac74089..a78a30375fe9 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -481,6 +481,7 @@ export default { flash: 'flash', shutter: 'obturador', gallery: 'galería', + addReceipt: 'Añadir recibo', }, iou: { amount: 'Importe', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 5a622852e2ef..71b150680716 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -47,6 +47,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ IOU_Send_Add_Debit_Card: () => require('../../../pages/settings/Wallet/AddDebitCardPage').default, IOU_Send_Enable_Payments: () => require('../../../pages/EnablePayments/EnablePaymentsPage').default, Money_Request_Waypoint: () => require('../../../pages/iou/WaypointEditorPage').default, + Money_Request_Receipt: () => require('../../../pages/EditRequestReceiptPage').default, Money_Request_Address: () => require('../../../pages/iou/DistanceRequestPage').default, }); diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 2841f53e7860..8c278e4aad59 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -325,6 +325,7 @@ export default { Money_Request_Tag: ROUTES.MONEY_REQUEST_TAG.route, Money_Request_Merchant: ROUTES.MONEY_REQUEST_MERCHANT.route, Money_Request_Waypoint: ROUTES.MONEY_REQUEST_WAYPOINT.route, + Money_Request_Receipt: ROUTES.MONEY_REQUEST_RECEIPT.route, Money_Request_Address: ROUTES.MONEY_REQUEST_ADDRESS.route, IOU_Send_Enable_Payments: ROUTES.IOU_SEND_ENABLE_PAYMENTS, IOU_Send_Add_Bank_Account: ROUTES.IOU_SEND_ADD_BANK_ACCOUNT, diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index b477624142ba..f6ef90bb7845 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2230,7 +2230,7 @@ function buildOptimisticIOUReportAction( created: DateUtils.getDBTime(), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, receipt, - whisperedToAccountIDs: !_.isEmpty(receipt) ? [currentUserAccountID] : [], + whisperedToAccountIDs: _.contains([CONST.IOU.RECEIPT_STATE.SCANREADY, CONST.IOU.RECEIPT_STATE.SCANNING], receipt.state) ? [currentUserAccountID] : [], }; } /** @@ -2283,6 +2283,7 @@ function buildOptimisticApprovedReportAction(amount, currency, expenseReportID) */ function buildOptimisticReportPreview(chatReport, iouReport, comment = '', transaction = undefined) { const hasReceipt = TransactionUtils.hasReceipt(transaction); + const isReceiptBeingScanned = hasReceipt && TransactionUtils.isReceiptBeingScanned(transaction); const message = getReportPreviewMessage(iouReport); return { reportActionID: NumberUtils.rand64(), @@ -2307,7 +2308,7 @@ function buildOptimisticReportPreview(chatReport, iouReport, comment = '', trans childMoneyRequestCount: 1, childLastMoneyRequestComment: comment, childLastReceiptTransactionIDs: hasReceipt ? transaction.transactionID : '', - whisperedToAccountIDs: hasReceipt ? [currentUserAccountID] : [], + whisperedToAccountIDs: isReceiptBeingScanned ? [currentUserAccountID] : [], }; } diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 7feca4524855..04b02977169d 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -699,6 +699,7 @@ function requestMoney( createdIOUReportActionID, reportPreviewReportActionID: reportPreviewAction.reportActionID, receipt, + receiptState: lodashGet(receipt, 'state'), category, tag, billable, @@ -2086,8 +2087,9 @@ function createEmptyTransaction() { * @param {String} iouType * @param {String} reportID * @param {Object} report + * @param {String} path */ -function navigateToNextPage(iou, iouType, reportID, report) { +function navigateToNextPage(iou, iouType, reportID, report, path = '') { const moneyRequestID = `${iouType}${reportID}`; const shouldReset = iou.id !== moneyRequestID; @@ -2097,6 +2099,12 @@ function navigateToNextPage(iou, iouType, reportID, report) { resetMoneyRequestInfo(moneyRequestID); } + // If we're adding a receipt, that means the user came from the confirmation page and we need to navigate back to it. + if (path.slice(1) === ROUTES.MONEY_REQUEST_RECEIPT.getRoute(iouType, reportID)) { + Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + return; + } + // If a request is initiated on a report, skip the participants selection step and navigate to the confirmation page. if (report.reportID) { // If the report is iou or expense report, we should get the chat report to set participant for request money diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js index f3c12fe2f9ee..f45085a052e9 100644 --- a/src/pages/EditRequestReceiptPage.js +++ b/src/pages/EditRequestReceiptPage.js @@ -21,7 +21,11 @@ const propTypes = { }).isRequired, /** The id of the transaction we're editing */ - transactionID: PropTypes.string.isRequired, + transactionID: PropTypes.string, +}; + +const defaultProps = { + transactionID: '', }; function EditRequestReceiptPage({route, transactionID}) { @@ -49,6 +53,7 @@ function EditRequestReceiptPage({route, transactionID}) { } EditRequestReceiptPage.propTypes = propTypes; +EditRequestReceiptPage.defaultProps = defaultProps; EditRequestReceiptPage.displayName = 'EditRequestReceiptPage'; export default EditRequestReceiptPage; diff --git a/src/pages/iou/ReceiptSelector/index.js b/src/pages/iou/ReceiptSelector/index.js index c8127f61eb32..a817195fe8a3 100644 --- a/src/pages/iou/ReceiptSelector/index.js +++ b/src/pages/iou/ReceiptSelector/index.js @@ -37,6 +37,9 @@ const propTypes = { /** The report ID of the IOU */ reportID: PropTypes.string, }), + + /** The current route path */ + path: PropTypes.string, }).isRequired, /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ @@ -124,7 +127,7 @@ function ReceiptSelector(props) { return; } - IOU.navigateToNextPage(iou, iouType, reportID, report); + IOU.navigateToNextPage(iou, iouType, reportID, report, props.route.path); }; return ( diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index c18d54b77741..a9d02a52411e 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -38,6 +38,9 @@ const propTypes = { /** The report ID of the IOU */ reportID: PropTypes.string, }), + + /** The current route path */ + path: PropTypes.string, }).isRequired, /** The report on which the request is initiated on */ @@ -220,13 +223,13 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) return; } - IOU.navigateToNextPage(iou, iouType, reportID, report); + IOU.navigateToNextPage(iou, iouType, reportID, report, route.path); }) .catch((error) => { showCameraAlert(); Log.warn('Error taking photo', error); }); - }, [flash, iouType, iou, report, reportID, translate, transactionID]); + }, [flash, iouType, iou, report, reportID, translate, transactionID, route.path]); CameraPermission.getCameraPermissionStatus().then((permissionStatus) => { setPermissions(permissionStatus); @@ -295,7 +298,7 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) return; } - IOU.navigateToNextPage(iou, iouType, reportID, report); + IOU.navigateToNextPage(iou, iouType, reportID, report, route.path); }) .catch(() => { Log.info('User did not select an image from gallery'); diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js index 9d4468c8aff4..015707db71f2 100644 --- a/src/pages/iou/steps/MoneyRequestConfirmPage.js +++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js @@ -27,6 +27,7 @@ import useNetwork from '../../../hooks/useNetwork'; import useWindowDimensions from '../../../hooks/useWindowDimensions'; import * as StyleUtils from '../../../styles/StyleUtils'; import {iouPropTypes, iouDefaultProps} from '../propTypes'; +import * as Expensicons from '../../../components/Icon/Expensicons'; const propTypes = { /** React Navigation route */ @@ -61,7 +62,7 @@ const defaultProps = { function MoneyRequestConfirmPage(props) { const {isOffline} = useNetwork(); - const {windowHeight} = useWindowDimensions(); + const {windowHeight, windowWidth} = useWindowDimensions(); const prevMoneyRequestId = useRef(props.iou.id); const iouType = useRef(lodashGet(props.route, 'params.iouType', '')); const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType.current, props.selectedTab); @@ -74,6 +75,7 @@ function MoneyRequestConfirmPage(props) { }), [props.iou.participants, props.personalDetails], ); + const isManualRequestDM = props.selectedTab === CONST.TAB.MANUAL && iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST; useEffect(() => { const policyExpenseChat = _.find(participants, (participant) => participant.isPolicyExpenseChat); @@ -214,7 +216,9 @@ function MoneyRequestConfirmPage(props) { } if (props.iou.receiptPath && props.iou.receiptSource) { - FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptSource).then((receipt) => { + FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptSource).then((file) => { + const receipt = file; + receipt.state = file && isManualRequestDM ? CONST.IOU.RECEIPT_STATE.OPEN : CONST.IOU.RECEIPT_STATE.SCANREADY; requestMoney(selectedParticipants, trimmedComment, receipt); }); return; @@ -238,6 +242,7 @@ function MoneyRequestConfirmPage(props) { isDistanceRequest, requestMoney, createDistanceRequest, + isManualRequestDM, ], ); @@ -286,6 +291,15 @@ function MoneyRequestConfirmPage(props) { Navigation.navigate(ROUTES.MONEY_REQUEST_RECEIPT.getRoute(iouType.current, reportID.current)), + }, + ]} /> {/* * The MoneyRequestConfirmationList component uses a SectionList which uses a VirtualizedList internally.