From d03ead5c55ed9e17567b907cddaaaea84a0a0358 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 3 Oct 2023 14:54:48 +0800 Subject: [PATCH 01/35] Display 'Split' bill button in Scan flow --- .../MoneyRequestParticipantsSelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js index 05b206ce4147..b79a33d73408 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js @@ -245,7 +245,7 @@ function MoneyRequestParticipantsSelector({ // the app from crashing on native when you try to do this, we'll going to hide the button if you have a workspace and other participants const hasPolicyExpenseChatParticipant = _.some(participants, (participant) => participant.isPolicyExpenseChat); const shouldShowConfirmButton = !(participants.length > 1 && hasPolicyExpenseChatParticipant); - const isAllowedToSplit = !isDistanceRequest && !isScanRequest; + const isAllowedToSplit = !isDistanceRequest; return ( 0 ? safeAreaPaddingBottomStyle : {}]}> From 6231e7ca52b6f71db9e284fb6c0af2e0e8048b63 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 3 Oct 2023 15:17:23 +0800 Subject: [PATCH 02/35] Hide $0 amounts in Split Bill --- src/components/MoneyRequestConfirmationList.js | 11 +++++++++-- src/libs/OptionsListUtils.js | 8 ++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index b71047e3ca36..b2f5d11d9406 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -243,7 +243,11 @@ function MoneyRequestConfirmationList(props) { const getParticipantsWithAmount = useCallback( (participantsList) => { const iouAmount = IOUUtils.calculateAmount(participantsList.length, props.iouAmount, props.iouCurrencyCode); - return OptionsListUtils.getIOUConfirmationOptionsFromParticipants(participantsList, CurrencyUtils.convertToDisplayString(iouAmount, props.iouCurrencyCode)); + return OptionsListUtils.getIOUConfirmationOptionsFromParticipants( + participantsList, + CurrencyUtils.convertToDisplayString(iouAmount, props.iouCurrencyCode), + props.iouAmount === 0, + ); }, [props.iouAmount, props.iouCurrencyCode], ); @@ -252,7 +256,9 @@ function MoneyRequestConfirmationList(props) { const splitOrRequestOptions = useMemo(() => { let text; - if (props.receiptPath || isDistanceRequestWithoutRoute) { + if (props.receiptPath && props.hasMultipleParticipants && props.iouAmount === 0) { + text = translate('iou.split'); + } else if (props.receiptPath || isDistanceRequestWithoutRoute) { text = translate('iou.request'); } else { const translationKey = props.hasMultipleParticipants ? 'iou.splitAmount' : 'iou.requestAmount'; @@ -289,6 +295,7 @@ function MoneyRequestConfirmationList(props) { const formattedPayeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( payeePersonalDetails, CurrencyUtils.convertToDisplayString(myIOUAmount, props.iouCurrencyCode), + props.iouAmount === 0, ); sections.push( diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 7832b9a180fe..829e0fe6747f 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1289,7 +1289,7 @@ function getSearchOptions(reports, personalDetails, searchValue = '', betas) { * @param {String} amountText * @returns {Object} */ -function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amountText) { +function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amountText, shouldHideDescriptiveText) { const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail.login); return { text: personalDetail.displayName || formattedLogin, @@ -1302,7 +1302,7 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amount id: personalDetail.accountID, }, ], - descriptiveText: amountText, + descriptiveText: shouldHideDescriptiveText ? '' : amountText, login: personalDetail.login, accountID: personalDetail.accountID, }; @@ -1315,10 +1315,10 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amount * @param {String} amountText * @returns {Array} */ -function getIOUConfirmationOptionsFromParticipants(participants, amountText) { +function getIOUConfirmationOptionsFromParticipants(participants, amountText, shouldHideDescriptiveText) { return _.map(participants, (participant) => ({ ...participant, - descriptiveText: amountText, + descriptiveText: shouldHideDescriptiveText ? '' : amountText, })); } From 60ffec470a828b27158298d77a0f7e868a24db13 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 3 Oct 2023 17:14:17 +0800 Subject: [PATCH 03/35] Add IOU action startSplitBillRequest --- src/libs/actions/IOU.js | 220 +++++++++++++++++- .../iou/steps/MoneyRequestConfirmPage.js | 16 ++ 2 files changed, 232 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 152f285c84af..b332043a349b 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -878,6 +878,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; + const shouldCreateSplitChatReport = _.isEmpty(existingSplitChatReportID); // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 const formattedParticipants = isOwnPolicyExpenseChat @@ -917,7 +918,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco splitChatReport.lastMessageHtml = splitIOUReportAction.message[0].html; // If we have an existing splitChatReport (group chat or workspace) use it's pending fields, otherwise indicate that we are adding a chat - if (!existingSplitChatReport) { + if (shouldCreateSplitChatReport) { splitChatReport.pendingFields = { createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }; @@ -927,12 +928,12 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco { // Use set for new reports because it doesn't exist yet, is faster, // and we need the data to be available when we navigate to the chat page - onyxMethod: existingSplitChatReport ? Onyx.METHOD.MERGE : Onyx.METHOD.SET, + onyxMethod: shouldCreateSplitChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, value: splitChatReport, }, { - onyxMethod: existingSplitChatReport ? Onyx.METHOD.MERGE : Onyx.METHOD.SET, + onyxMethod: shouldCreateSplitChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { ...(existingSplitChatReport ? {} : {[splitCreatedReportAction.reportActionID]: splitCreatedReportAction}), @@ -951,7 +952,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { - ...(existingSplitChatReport ? {} : {[splitCreatedReportAction.reportActionID]: {pendingAction: null}}), + ...(shouldCreateSplitChatReport ? {[splitCreatedReportAction.reportActionID]: {pendingAction: null}} : {}), [splitIOUReportAction.reportActionID]: {pendingAction: null}, }, }, @@ -1040,10 +1041,15 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco if ((!hasMultipleParticipants && !existingSplitChatReportID) || isOwnPolicyExpenseChat) { oneOnOneChatReport = splitChatReport; shouldCreateOptimisticPersonalDetails = !existingSplitChatReport; + console.log(shouldCreateOptimisticPersonalDetails); } else { const existingChatReport = ReportUtils.getChatByParticipants([accountID]); isNewOneOnOneChatReport = !existingChatReport; shouldCreateOptimisticPersonalDetails = isNewOneOnOneChatReport; + if (!existingChatReport) { + console.log('building chat report'); + } + console.log(shouldCreateOptimisticPersonalDetails); oneOnOneChatReport = existingChatReport || ReportUtils.buildOptimisticChatReport([accountID]); } @@ -1231,6 +1237,211 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou Report.notifyNewAction(splitData.chatReportID, currentUserAccountID); } +/** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned */ +function startSplitBillRequest(participants, currentUserLogin, currentUserAccountID, comment, receipt, existingSplitChatReportID = '') { + const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin); + const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID)); + const existingSplitChatReport = + existingSplitChatReportID || participants[0].reportID + ? allReports[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] + : ReportUtils.getChatByParticipants(participantAccountIDs); + const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); + const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; + const shouldCreateSplitChatReport = _.isEmpty(existingSplitChatReportID); + + const receiptObject = {}; + let filename; + if (receipt && receipt.source) { + receiptObject.source = receipt.source; + receiptObject.state = receipt.state || CONST.IOU.RECEIPT_STATE.SCANREADY; + filename = receipt.name; + } + + // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 + const splitTransaction = TransactionUtils.buildOptimisticTransaction(0, undefined, CONST.REPORT.SPLIT_REPORTID, comment, '', '', '', '', receiptObject, filename); + + // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat + const splitChatCreatedReportAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); + const splitIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( + CONST.IOU.REPORT_ACTION_TYPE.SPLIT, + 0, + CONST.CURRENCY.USD, + comment, + participants, + splitTransaction.transactionID, + '', + '', + false, + false, + receiptObject, + isOwnPolicyExpenseChat, + ); + + splitChatReport.lastReadTime = DateUtils.getDBTime(); + splitChatReport.lastMessageText = splitIOUReportAction.message[0].text; + splitChatReport.lastMessageHtml = splitIOUReportAction.message[0].html; + + // If we have an existing splitChatReport (group chat or workspace) use it's pending fields, otherwise indicate that we are adding a chat + if (shouldCreateSplitChatReport) { + splitChatReport.pendingFields = { + createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }; + } + + const optimisticData = [ + { + // Use set for new reports because it doesn't exist yet, is faster, + // and we need the data to be available when we navigate to the chat page + onyxMethod: shouldCreateSplitChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, + value: splitChatReport, + }, + { + onyxMethod: shouldCreateSplitChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, + value: { + ...(shouldCreateSplitChatReport ? {[splitChatCreatedReportAction.reportActionID]: splitChatCreatedReportAction} : {}), + [splitIOUReportAction.reportActionID]: splitIOUReportAction, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, + value: splitTransaction, + }, + ]; + + const successData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, + value: { + ...(shouldCreateSplitChatReport ? {[splitChatCreatedReportAction.reportActionID]: {pendingAction: null}} : {}), + [splitIOUReportAction.reportActionID]: {pendingAction: null}, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, + value: {pendingAction: null}, + }, + ]; + + if (shouldCreateSplitChatReport) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, + value: {pendingFields: {createChat: null}}, + }); + } + + const failureData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, + value: { + errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + }, + }, + ]; + + if (existingSplitChatReport) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, + value: { + [splitIOUReportAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + }, + }, + }); + } else { + failureData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, + value: { + errorFields: { + createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, + value: { + [splitIOUReportAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError(null), + }, + }, + }, + ); + } + + const splits = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID}]; + + _.each(participants, (participant) => { + const email = participant.isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase(); + const accountID = participant.isOwnPolicyExpenseChat ? 0 : Number(participant.accountID); + if (email === currentUserEmailForIOUSplit) { + return; + } + + // When splitting with a workspace chat, we only need to supply the policyID + if (participant.isOwnPolicyExpenseChat) { + splits.push({ + policyID: participant.policyID, + }); + return; + } + + // Add optimistic personal details for new participants + if (shouldCreateSplitChatReport) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: { + [accountID]: { + accountID, + avatar: UserUtils.getDefaultAvatarURL(accountID), + displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), + login: participant.login, + }, + }, + }); + } + + splits.push({ + email, + accountID, + }); + }); + console.log('split chat report', splitChatReport); + + API.write( + 'StartSplitBillRequest', + { + chatReportID: splitChatReport.reportID, + reportActionID: splitIOUReportAction.reportActionID, + transactionID: splitTransaction.transactionID, + splits: JSON.stringify(splits), + receipt, + comment, + shouldCreateSplitChatReport, + ...(shouldCreateSplitChatReport ? {createdReportActionID: splitChatCreatedReportAction.reportActionID} : {}), + }, + {optimisticData, successData, failureData}, + ); + + resetMoneyRequestInfo(); + if (existingSplitChatReport) { + Navigation.dismissModal(existingSplitChatReport.reportID); + } else { + Navigation.dismissModal(); + } + Report.notifyNewAction(splitChatReport.chatReportID, currentUserAccountID); +} + /** * @param {String} transactionID * @param {Number} transactionThreadReportID @@ -2290,6 +2501,7 @@ export { deleteMoneyRequest, splitBill, splitBillAndOpenReport, + startSplitBillRequest, requestMoney, sendMoneyElsewhere, approveMoneyRequest, diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js index 3881221d5c52..42f8766715bd 100644 --- a/src/pages/iou/steps/MoneyRequestConfirmPage.js +++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js @@ -188,6 +188,22 @@ function MoneyRequestConfirmPage(props) { (selectedParticipants) => { const trimmedComment = props.iou.comment.trim(); + // If we have a receipt let's start the split bill by creating only the action, the transaction, and the group DM if needed + if (iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT && props.iou.receiptPath) { + const existingSplitChatReportID = CONST.REGEX.NUMBER.test(reportID.current) ? reportID.current : ''; + FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptSource).then((receipt) => { + IOU.startSplitBillRequest( + selectedParticipants, + props.currentUserPersonalDetails.login, + props.currentUserPersonalDetails.accountID, + trimmedComment, + receipt, + existingSplitChatReportID, + ); + }); + return; + } + // IOUs created from a group report will have a reportID param in the route. // Since the user is already viewing the report, we don't need to navigate them to the report if (iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT && CONST.REGEX.NUMBER.test(reportID.current)) { From facfd1635fd826ad0d606936f9e702fe989c6b76 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 3 Oct 2023 17:43:13 +0800 Subject: [PATCH 04/35] Hide split amount if it's O --- src/components/ReportActionItem/MoneyRequestPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 35215cadd15d..b4101d9b5721 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -317,7 +317,7 @@ function MoneyRequestPreview(props) { )} {shouldShowDescription && {description}} - {props.isBillSplit && !_.isEmpty(participantAccountIDs) && ( + {props.isBillSplit && !_.isEmpty(participantAccountIDs) && requestAmount > 0 && ( {props.translate('iou.amountEach', { amount: CurrencyUtils.convertToDisplayString( From ec7610da9267df746248f7851991b9580bcef384 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 3 Oct 2023 17:43:25 +0800 Subject: [PATCH 05/35] Add receipt status text in SplitBillDetailsPage --- src/components/MoneyRequestHeaderStatusBar.js | 2 +- src/pages/iou/SplitBillDetailsPage.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestHeaderStatusBar.js b/src/components/MoneyRequestHeaderStatusBar.js index a4f21e3d168d..499fa1433e05 100644 --- a/src/components/MoneyRequestHeaderStatusBar.js +++ b/src/components/MoneyRequestHeaderStatusBar.js @@ -8,7 +8,7 @@ function MoneyRequestHeaderStatusBar() { const {translate} = useLocalize(); return ( - + {translate('iou.receiptStatusTitle')} diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js index 23da92bcc9e7..65cf6f85474c 100644 --- a/src/pages/iou/SplitBillDetailsPage.js +++ b/src/pages/iou/SplitBillDetailsPage.js @@ -19,6 +19,7 @@ import CONST from '../../CONST'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; import * as TransactionUtils from '../../libs/TransactionUtils'; import * as ReportUtils from '../../libs/ReportUtils'; +import MoneyRequestHeaderStatusBar from '../../components/MoneyRequestHeaderStatusBar'; const propTypes = { /* Onyx Props */ @@ -79,6 +80,7 @@ function SplitBillDetailsPage(props) { pointerEvents="box-none" style={[styles.containerWithSpaceBetween]} > + {Boolean(participants.length) && ( Date: Wed, 4 Oct 2023 00:18:52 +0800 Subject: [PATCH 06/35] Allow editing merchant and date when creating split bill --- src/components/MoneyRequestConfirmationList.js | 4 ++-- src/libs/TransactionUtils.ts | 12 ++++++++++-- src/libs/actions/IOU.js | 10 ++++++---- src/pages/iou/SplitBillDetailsPage.js | 6 ++++-- src/pages/iou/steps/MoneyRequestConfirmPage.js | 4 +++- 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index b2f5d11d9406..71620a1f07d9 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -539,7 +539,7 @@ function MoneyRequestConfirmationList(props) { style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_DATE.getRoute(props.iouType, props.reportID))} - disabled={didConfirm || props.isReadOnly || !isTypeRequest} + disabled={didConfirm || props.isReadOnly} /> {props.isDistanceRequest ? ( Navigation.navigate(ROUTES.MONEY_REQUEST_MERCHANT.getRoute(props.iouType, props.reportID))} - disabled={didConfirm || props.isReadOnly || !isTypeRequest} + disabled={didConfirm || props.isReadOnly} /> )} {shouldShowCategories && ( diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 4a9ab448546a..9fd8804d73cd 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -220,7 +220,10 @@ function getCurrency(transaction: Transaction): string { * Return the merchant field from the transaction, return the modifiedMerchant if present. */ function getMerchant(transaction: Transaction): string { - return transaction?.modifiedMerchant ?? transaction?.merchant ?? ''; + if (transaction?.modifiedMerchant?.length) { + return transaction.modifiedMerchant; + } + return transaction?.merchant ?? ''; } /** @@ -255,7 +258,12 @@ function getTag(transaction: Transaction): string { * Return the created field from the transaction, return the modifiedCreated if present. */ function getCreated(transaction: Transaction): string { - const created = transaction?.modifiedCreated ?? transaction?.created ?? ''; + let created: string; + if (transaction?.modifiedCreated?.length) { + created = transaction.modifiedCreated; + } else { + created = transaction?.created ?? ''; + } const createdDate = parseISO(created); if (isValid(createdDate)) { return format(createdDate, CONST.DATE.FNS_FORMAT_STRING); diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index b332043a349b..b1990ed66424 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1238,7 +1238,7 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou } /** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned */ -function startSplitBillRequest(participants, currentUserLogin, currentUserAccountID, comment, receipt, existingSplitChatReportID = '') { +function startSplitBill(participants, currentUserLogin, currentUserAccountID, comment, created, merchant, receipt, existingSplitChatReportID = '') { const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID)); const existingSplitChatReport = @@ -1258,7 +1258,7 @@ function startSplitBillRequest(participants, currentUserLogin, currentUserAccoun } // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 - const splitTransaction = TransactionUtils.buildOptimisticTransaction(0, undefined, CONST.REPORT.SPLIT_REPORTID, comment, '', '', '', '', receiptObject, filename); + const splitTransaction = TransactionUtils.buildOptimisticTransaction(0, undefined, CONST.REPORT.SPLIT_REPORTID, comment, created, '', '', merchant, receiptObject, filename); // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat const splitChatCreatedReportAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); @@ -1419,7 +1419,7 @@ function startSplitBillRequest(participants, currentUserLogin, currentUserAccoun console.log('split chat report', splitChatReport); API.write( - 'StartSplitBillRequest', + 'StartSplitBill', { chatReportID: splitChatReport.reportID, reportActionID: splitIOUReportAction.reportActionID, @@ -1427,6 +1427,8 @@ function startSplitBillRequest(participants, currentUserLogin, currentUserAccoun splits: JSON.stringify(splits), receipt, comment, + created, + merchant, shouldCreateSplitChatReport, ...(shouldCreateSplitChatReport ? {createdReportActionID: splitChatCreatedReportAction.reportActionID} : {}), }, @@ -2501,7 +2503,7 @@ export { deleteMoneyRequest, splitBill, splitBillAndOpenReport, - startSplitBillRequest, + startSplitBill, requestMoney, sendMoneyElsewhere, approveMoneyRequest, diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js index 65cf6f85474c..d694b83b9447 100644 --- a/src/pages/iou/SplitBillDetailsPage.js +++ b/src/pages/iou/SplitBillDetailsPage.js @@ -70,7 +70,7 @@ function SplitBillDetailsPage(props) { } const payeePersonalDetails = props.personalDetails[reportAction.actorAccountID]; const participantsExcludingPayee = _.filter(participants, (participant) => participant.accountID !== reportAction.actorAccountID); - const {amount: splitAmount, currency: splitCurrency, comment: splitComment} = ReportUtils.getTransactionDetails(transaction); + const {amount: splitAmount, currency: splitCurrency, comment: splitComment, merchant: splitMerchant, created: splitCreated} = ReportUtils.getTransactionDetails(transaction); return ( @@ -87,8 +87,10 @@ function SplitBillDetailsPage(props) { payeePersonalDetails={payeePersonalDetails} selectedParticipants={participantsExcludingPayee} iouAmount={splitAmount} - iouComment={splitComment} iouCurrencyCode={splitCurrency} + iouComment={splitComment} + iouCreated={splitCreated} + iouMerchant={splitMerchant} iouType={CONST.IOU.MONEY_REQUEST_TYPE.SPLIT} isReadOnly shouldShowFooter={false} diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js index 42f8766715bd..5a97e6cf18b0 100644 --- a/src/pages/iou/steps/MoneyRequestConfirmPage.js +++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js @@ -192,11 +192,13 @@ function MoneyRequestConfirmPage(props) { if (iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT && props.iou.receiptPath) { const existingSplitChatReportID = CONST.REGEX.NUMBER.test(reportID.current) ? reportID.current : ''; FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptSource).then((receipt) => { - IOU.startSplitBillRequest( + IOU.startSplitBill( selectedParticipants, props.currentUserPersonalDetails.login, props.currentUserPersonalDetails.accountID, trimmedComment, + props.iou.created, + props.iou.merchant, receipt, existingSplitChatReportID, ); From 736198565f83e1843fd5296961bda3272a9802e9 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Wed, 4 Oct 2023 12:19:25 +0800 Subject: [PATCH 07/35] Fix navigation to newly created chat --- src/libs/actions/IOU.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index b1990ed66424..c167fc463412 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1436,8 +1436,8 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co ); resetMoneyRequestInfo(); - if (existingSplitChatReport) { - Navigation.dismissModal(existingSplitChatReport.reportID); + if (shouldCreateSplitChatReport) { + Navigation.dismissModal(splitChatReport.reportID); } else { Navigation.dismissModal(); } From 04ec01c5eb69f25532a22babfb744817a2a19a17 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Wed, 4 Oct 2023 13:10:00 +0800 Subject: [PATCH 08/35] Show TabSelector when using Split Bill from workspace chat --- src/pages/iou/MoneyRequestSelectorPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/MoneyRequestSelectorPage.js b/src/pages/iou/MoneyRequestSelectorPage.js index c8e3aebaa0b3..d006e3480a4e 100644 --- a/src/pages/iou/MoneyRequestSelectorPage.js +++ b/src/pages/iou/MoneyRequestSelectorPage.js @@ -99,7 +99,7 @@ function MoneyRequestSelectorPage(props) { title={title[iouType]} onBackButtonPress={Navigation.dismissModal} /> - {iouType === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST ? ( + {iouType === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST || iouType === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT ? ( Date: Wed, 4 Oct 2023 13:17:11 +0800 Subject: [PATCH 09/35] Display receipt in SplitBillDetailsPage --- src/components/MoneyRequestConfirmationList.js | 5 ++++- src/pages/iou/SplitBillDetailsPage.js | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 71620a1f07d9..b43543a2ccaf 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -464,6 +464,8 @@ function MoneyRequestConfirmationList(props) { ); }, [confirm, props.bankAccountRoute, props.iouCurrencyCode, props.iouType, props.isReadOnly, props.policyID, selectedParticipants, splitOrRequestOptions, translate, formError]); + const {image, thumbnail} = props.receiptPath && ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptSource); + return ( ) : ( )} From fbdf93d97f33367e15a919f3899e726c38f9223c Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Thu, 5 Oct 2023 02:52:34 +0800 Subject: [PATCH 10/35] Cleanup --- src/components/MoneyRequestConfirmationList.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index b43543a2ccaf..2c5df762abbd 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -464,7 +464,7 @@ function MoneyRequestConfirmationList(props) { ); }, [confirm, props.bankAccountRoute, props.iouCurrencyCode, props.iouType, props.isReadOnly, props.policyID, selectedParticipants, splitOrRequestOptions, translate, formError]); - const {image, thumbnail} = props.receiptPath && ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptSource); + const {image: receiptImage, thumbnail: receiptThumbnail} = props.receiptPath && ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptSource); return ( )} - {!_.isEmpty(props.receiptPath) ? ( + {receiptImage || receiptThumbnail ? ( ) : ( Date: Thu, 5 Oct 2023 03:22:55 +0800 Subject: [PATCH 11/35] Fix splitting bill with unkown users --- src/libs/actions/IOU.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index c167fc463412..021d1e41b7b1 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1239,6 +1239,7 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou /** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned */ function startSplitBill(participants, currentUserLogin, currentUserAccountID, comment, created, merchant, receipt, existingSplitChatReportID = '') { + console.log(participants); const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID)); const existingSplitChatReport = @@ -1381,7 +1382,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co const splits = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID}]; _.each(participants, (participant) => { - const email = participant.isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase(); + const email = participant.isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login || participant.text).toLowerCase(); const accountID = participant.isOwnPolicyExpenseChat ? 0 : Number(participant.accountID); if (email === currentUserEmailForIOUSplit) { return; @@ -1405,7 +1406,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co accountID, avatar: UserUtils.getDefaultAvatarURL(accountID), displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), - login: participant.login, + login: participant.login || participant.text, }, }, }); From a51725a1cd65f2b60ec79f9aff59ac64438dd23b Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Thu, 5 Oct 2023 03:37:32 +0800 Subject: [PATCH 12/35] Cleanup --- src/libs/TransactionUtils.ts | 4 ---- src/libs/actions/IOU.js | 16 ++++------------ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index dcd145445130..9fd8804d73cd 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -220,14 +220,10 @@ function getCurrency(transaction: Transaction): string { * Return the merchant field from the transaction, return the modifiedMerchant if present. */ function getMerchant(transaction: Transaction): string { -<<<<<<< HEAD if (transaction?.modifiedMerchant?.length) { return transaction.modifiedMerchant; } return transaction?.merchant ?? ''; -======= - return transaction?.modifiedMerchant ? transaction.modifiedMerchant : transaction?.merchant || ''; ->>>>>>> main } /** diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 8f75372b8c26..687d58c8b53c 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -895,7 +895,6 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; - const shouldCreateSplitChatReport = _.isEmpty(existingSplitChatReportID); // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 const formattedParticipants = isOwnPolicyExpenseChat @@ -935,7 +934,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco splitChatReport.lastMessageHtml = splitIOUReportAction.message[0].html; // If we have an existing splitChatReport (group chat or workspace) use it's pending fields, otherwise indicate that we are adding a chat - if (shouldCreateSplitChatReport) { + if (!existingSplitChatReport) { splitChatReport.pendingFields = { createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }; @@ -945,12 +944,12 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco { // Use set for new reports because it doesn't exist yet, is faster, // and we need the data to be available when we navigate to the chat page - onyxMethod: shouldCreateSplitChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, + onyxMethod: existingSplitChatReport ? Onyx.METHOD.MERGE : Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, value: splitChatReport, }, { - onyxMethod: shouldCreateSplitChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, + onyxMethod: existingSplitChatReport ? Onyx.METHOD.MERGE : Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { ...(existingSplitChatReport ? {} : {[splitCreatedReportAction.reportActionID]: splitCreatedReportAction}), @@ -969,7 +968,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { - ...(shouldCreateSplitChatReport ? {[splitCreatedReportAction.reportActionID]: {pendingAction: null}} : {}), + ...(existingSplitChatReport ? {} : {[splitCreatedReportAction.reportActionID]: {pendingAction: null}}), [splitIOUReportAction.reportActionID]: {pendingAction: null}, }, }, @@ -1058,15 +1057,10 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco if ((!hasMultipleParticipants && !existingSplitChatReportID) || isOwnPolicyExpenseChat) { oneOnOneChatReport = splitChatReport; shouldCreateOptimisticPersonalDetails = !existingSplitChatReport; - console.log(shouldCreateOptimisticPersonalDetails); } else { const existingChatReport = ReportUtils.getChatByParticipants([accountID]); isNewOneOnOneChatReport = !existingChatReport; shouldCreateOptimisticPersonalDetails = isNewOneOnOneChatReport; - if (!existingChatReport) { - console.log('building chat report'); - } - console.log(shouldCreateOptimisticPersonalDetails); oneOnOneChatReport = existingChatReport || ReportUtils.buildOptimisticChatReport([accountID]); } @@ -1257,7 +1251,6 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou /** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned */ function startSplitBill(participants, currentUserLogin, currentUserAccountID, comment, created, merchant, receipt, existingSplitChatReportID = '') { - console.log(participants); const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID)); const existingSplitChatReport = @@ -1435,7 +1428,6 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co accountID, }); }); - console.log('split chat report', splitChatReport); API.write( 'StartSplitBill', From 3820323ff11558ced3c930b9c2a2823742592a97 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Thu, 5 Oct 2023 03:41:21 +0800 Subject: [PATCH 13/35] Add JSDocs --- src/libs/actions/IOU.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 687d58c8b53c..ef7d2071faf5 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1249,7 +1249,17 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou Report.notifyNewAction(splitData.chatReportID, currentUserAccountID); } -/** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned */ +/** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned + * + * @param {Array} participants + * @param {String} currentUserLogin + * @param {Number} currentUserAccountID + * @param {String} comment + * @param {String} created + * @param {String} merchant + * @param {Object} receipt + * @param {String} existingSplitChatReportID - Either a group DM or a workspace chat + */ function startSplitBill(participants, currentUserLogin, currentUserAccountID, comment, created, merchant, receipt, existingSplitChatReportID = '') { const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID)); From 51fbbf6f18a35b9166ff26dabb2887a1fd09faf1 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Thu, 5 Oct 2023 11:34:15 +0800 Subject: [PATCH 14/35] Remove created and merchant --- .../MoneyRequestConfirmationList.js | 25 +++++++++++-------- src/libs/actions/IOU.js | 6 ++--- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 2c5df762abbd..0910ff3302e1 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -535,16 +535,18 @@ function MoneyRequestConfirmationList(props) { )} {shouldShowAllFields && ( <> - Navigation.navigate(ROUTES.MONEY_REQUEST_DATE.getRoute(props.iouType, props.reportID))} - disabled={didConfirm || props.isReadOnly} - /> - {props.isDistanceRequest ? ( + {!props.receiptPath && ( + Navigation.navigate(ROUTES.MONEY_REQUEST_DATE.getRoute(props.iouType, props.reportID))} + disabled={didConfirm || props.isReadOnly} + /> + )} + {props.isDistanceRequest && ( Navigation.navigate(ROUTES.MONEY_REQUEST_DISTANCE.getRoute(props.iouType, props.reportID))} disabled={didConfirm || props.isReadOnly || !isTypeRequest} /> - ) : ( + )} + {!props.isDistanceRequest && !props.receiptPath && ( Number(participant.accountID)); const existingSplitChatReport = @@ -1280,7 +1280,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co } // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 - const splitTransaction = TransactionUtils.buildOptimisticTransaction(0, undefined, CONST.REPORT.SPLIT_REPORTID, comment, created, '', '', merchant, receiptObject, filename); + const splitTransaction = TransactionUtils.buildOptimisticTransaction(0, undefined, CONST.REPORT.SPLIT_REPORTID, comment, '', '', '', '', receiptObject, filename); // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat const splitChatCreatedReportAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); @@ -1448,8 +1448,6 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co splits: JSON.stringify(splits), receipt, comment, - created, - merchant, shouldCreateSplitChatReport, ...(shouldCreateSplitChatReport ? {createdReportActionID: splitChatCreatedReportAction.reportActionID} : {}), }, From 6f8b80875f03848b0b77ac1a1519fd1d5dbf7274 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Thu, 5 Oct 2023 12:33:07 +0800 Subject: [PATCH 15/35] Only display scan status when receipt is being scanned --- src/pages/iou/SplitBillDetailsPage.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js index 81d681604410..205814e2c7e2 100644 --- a/src/pages/iou/SplitBillDetailsPage.js +++ b/src/pages/iou/SplitBillDetailsPage.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useMemo} from 'react'; import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; @@ -71,6 +71,7 @@ function SplitBillDetailsPage(props) { const payeePersonalDetails = props.personalDetails[reportAction.actorAccountID]; const participantsExcludingPayee = _.filter(participants, (participant) => participant.accountID !== reportAction.actorAccountID); const {amount: splitAmount, currency: splitCurrency, comment: splitComment, merchant: splitMerchant, created: splitCreated} = ReportUtils.getTransactionDetails(transaction); + const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction); return ( @@ -80,7 +81,7 @@ function SplitBillDetailsPage(props) { pointerEvents="box-none" style={[styles.containerWithSpaceBetween]} > - + {isScanning && } {Boolean(participants.length) && ( Date: Thu, 5 Oct 2023 12:36:27 +0800 Subject: [PATCH 16/35] Cleanup --- src/components/MoneyRequestConfirmationList.js | 2 +- src/libs/OptionsListUtils.js | 2 ++ src/pages/iou/steps/MoneyRequestConfirmPage.js | 2 -- .../MoneyRequestParticipantsSelector.js | 5 ----- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 0910ff3302e1..749e1826ca80 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -493,7 +493,7 @@ function MoneyRequestConfirmationList(props) { {receiptImage || receiptThumbnail ? ( ) : ( diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 6170c2808a44..601d68aceea9 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1300,6 +1300,7 @@ function getSearchOptions(reports, personalDetails, searchValue = '', betas) { * * @param {Object} personalDetail * @param {String} amountText + * @param {Boolean} shouldHideDescriptiveText * @returns {Object} */ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amountText, shouldHideDescriptiveText) { @@ -1326,6 +1327,7 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amount * * @param {Array} participants * @param {String} amountText + * @param {Boolean} shouldHideDescriptiveText * @returns {Array} */ function getIOUConfirmationOptionsFromParticipants(participants, amountText, shouldHideDescriptiveText) { diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js index 5a97e6cf18b0..9e55bb21e36a 100644 --- a/src/pages/iou/steps/MoneyRequestConfirmPage.js +++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js @@ -197,8 +197,6 @@ function MoneyRequestConfirmPage(props) { props.currentUserPersonalDetails.login, props.currentUserPersonalDetails.accountID, trimmedComment, - props.iou.created, - props.iou.merchant, receipt, existingSplitChatReportID, ); diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js index b79a33d73408..ee5629b9394c 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js @@ -59,9 +59,6 @@ const propTypes = { /** Whether the money request is a distance request or not */ isDistanceRequest: PropTypes.bool, - /** Whether the money request is a scan request or not */ - isScanRequest: PropTypes.bool, - ...withLocalizePropTypes, }; @@ -73,7 +70,6 @@ const defaultProps = { reports: {}, betas: [], isDistanceRequest: false, - isScanRequest: false, }; function MoneyRequestParticipantsSelector({ @@ -89,7 +85,6 @@ function MoneyRequestParticipantsSelector({ safeAreaPaddingBottomStyle, iouType, isDistanceRequest, - isScanRequest, }) { const [searchTerm, setSearchTerm] = useState(''); const [newChatOptions, setNewChatOptions] = useState({ From a1b79b05205ed3101297ada47ec5fe4ff3dd74e3 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Thu, 5 Oct 2023 12:54:19 +0800 Subject: [PATCH 17/35] Fix GH actions --- src/components/MoneyRequestConfirmationList.js | 2 +- src/libs/actions/IOU.js | 3 +-- src/pages/iou/SplitBillDetailsPage.js | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 749e1826ca80..ee1f20aa5812 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -270,7 +270,7 @@ function MoneyRequestConfirmationList(props) { value: props.hasMultipleParticipants ? CONST.IOU.MONEY_REQUEST_TYPE.SPLIT : CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, }, ]; - }, [props.hasMultipleParticipants, props.receiptPath, translate, formattedAmount, isDistanceRequestWithoutRoute]); + }, [props.hasMultipleParticipants, props.iouAmount, props.receiptPath, translate, formattedAmount, isDistanceRequestWithoutRoute]); const selectedParticipants = useMemo(() => _.filter(props.selectedParticipants, (participant) => participant.selected), [props.selectedParticipants]); const payeePersonalDetails = useMemo(() => props.payeePersonalDetails || props.currentUserPersonalDetails, [props.payeePersonalDetails, props.currentUserPersonalDetails]); diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d8925a45cc09..e2222419c141 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1250,13 +1250,12 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou } /** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned + * or user enters details manually. * * @param {Array} participants * @param {String} currentUserLogin * @param {Number} currentUserAccountID * @param {String} comment - * @param {String} created - * @param {String} merchant * @param {Object} receipt * @param {String} existingSplitChatReportID - Either a group DM or a workspace chat */ diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js index 205814e2c7e2..f073615116ef 100644 --- a/src/pages/iou/SplitBillDetailsPage.js +++ b/src/pages/iou/SplitBillDetailsPage.js @@ -1,4 +1,4 @@ -import React, {useMemo} from 'react'; +import React from 'react'; import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; From 2af470bb8573df4d831f602d1db085f3f89c32fe Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Fri, 6 Oct 2023 12:16:48 +0800 Subject: [PATCH 18/35] Sve policy expense chat reportID in splits array --- src/libs/actions/IOU.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index e2222419c141..2235704cf299 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1037,7 +1037,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco const hasMultipleParticipants = participants.length > 1; _.each(participants, (participant) => { // In case the participant is a worskapce, email & accountID should remain undefined and won't be used in the rest of this code - const email = isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase(); + const email = isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login || participant.text).toLowerCase(); const accountID = isOwnPolicyExpenseChat ? 0 : Number(participant.accountID); if (email === currentUserEmailForIOUSplit) { return; @@ -1408,10 +1408,11 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co return; } - // When splitting with a workspace chat, we only need to supply the policyID + // When splitting with a workspace chat, we only need to supply the policyID and the workspace reportID as it's needed so we can update the report preview if (participant.isOwnPolicyExpenseChat) { splits.push({ policyID: participant.policyID, + chatReportID: splitChatReport.reportID, }); return; } From 53f28f3199074d6382ca615edec52e279acb582b Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Fri, 6 Oct 2023 20:11:09 +0800 Subject: [PATCH 19/35] Create optimistic details of participants if needed --- src/libs/actions/IOU.js | 42 ++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 2235704cf299..28307fce4384 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -23,6 +23,14 @@ import * as NumberUtils from '../NumberUtils'; import ReceiptGeneric from '../../../assets/images/receipt-generic.png'; import * as LocalePhoneNumber from '../LocalePhoneNumber'; +let allPersonalDetails; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (val) => { + allPersonalDetails = val || {}; + }, +}); + let allReports; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -1268,7 +1276,6 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; - const shouldCreateSplitChatReport = _.isEmpty(existingSplitChatReportID); const receiptObject = {}; let filename; @@ -1279,7 +1286,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co } // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 - const splitTransaction = TransactionUtils.buildOptimisticTransaction(0, undefined, CONST.REPORT.SPLIT_REPORTID, comment, '', '', '', '', receiptObject, filename); + const splitTransaction = TransactionUtils.buildOptimisticTransaction(0, CONST.CURRENCY.USD, CONST.REPORT.SPLIT_REPORTID, comment, '', '', '', '', receiptObject, filename); // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat const splitChatCreatedReportAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); @@ -1303,7 +1310,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co splitChatReport.lastMessageHtml = splitIOUReportAction.message[0].html; // If we have an existing splitChatReport (group chat or workspace) use it's pending fields, otherwise indicate that we are adding a chat - if (shouldCreateSplitChatReport) { + if (!existingSplitChatReport) { splitChatReport.pendingFields = { createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }; @@ -1313,15 +1320,15 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co { // Use set for new reports because it doesn't exist yet, is faster, // and we need the data to be available when we navigate to the chat page - onyxMethod: shouldCreateSplitChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, + onyxMethod: existingSplitChatReport ? Onyx.METHOD.MERGE : Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, value: splitChatReport, }, { - onyxMethod: shouldCreateSplitChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, + onyxMethod: existingSplitChatReport ? Onyx.METHOD.MERGE : Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { - ...(shouldCreateSplitChatReport ? {[splitChatCreatedReportAction.reportActionID]: splitChatCreatedReportAction} : {}), + ...(!existingSplitChatReport ? {} : {[splitChatCreatedReportAction.reportActionID]: splitChatCreatedReportAction}), [splitIOUReportAction.reportActionID]: splitIOUReportAction, }, }, @@ -1337,7 +1344,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { - ...(shouldCreateSplitChatReport ? {[splitChatCreatedReportAction.reportActionID]: {pendingAction: null}} : {}), + ...(!existingSplitChatReport ? {} : {[splitChatCreatedReportAction.reportActionID]: {pendingAction: null}}), [splitIOUReportAction.reportActionID]: {pendingAction: null}, }, }, @@ -1348,7 +1355,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co }, ]; - if (shouldCreateSplitChatReport) { + if (!existingSplitChatReport) { successData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, @@ -1392,7 +1399,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { [splitIOUReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError(null), + errors: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), }, }, }, @@ -1417,8 +1424,8 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co return; } - // Add optimistic personal details for new participants - if (shouldCreateSplitChatReport) { + const participantPersonalDetails = allPersonalDetails[participant.accountID]; + if (!participantPersonalDetails) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS_LIST, @@ -1427,7 +1434,8 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co accountID, avatar: UserUtils.getDefaultAvatarURL(accountID), displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), - login: participant.login || participant.text, + login: participant.login, + isOptimisticPersonalDetail: true, }, }, }); @@ -1448,17 +1456,17 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co splits: JSON.stringify(splits), receipt, comment, - shouldCreateSplitChatReport, - ...(shouldCreateSplitChatReport ? {createdReportActionID: splitChatCreatedReportAction.reportActionID} : {}), + isFromGroupDM: !existingSplitChatReport, + ...(!existingSplitChatReport ? {} : {createdReportActionID: splitChatCreatedReportAction.reportActionID}), }, {optimisticData, successData, failureData}, ); resetMoneyRequestInfo(); - if (shouldCreateSplitChatReport) { - Navigation.dismissModal(splitChatReport.reportID); - } else { + if (existingSplitChatReport) { Navigation.dismissModal(); + } else { + Navigation.dismissModal(splitChatReport.reportID); } Report.notifyNewAction(splitChatReport.chatReportID, currentUserAccountID); } From 027cc049319c496511e4b7a08bb707008005b416 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Sat, 7 Oct 2023 01:34:02 +0800 Subject: [PATCH 20/35] Show amount in split details page once it's created --- src/components/MoneyRequestConfirmationList.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index ee1f20aa5812..7f1bacc1544d 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -199,6 +199,8 @@ function MoneyRequestConfirmationList(props) { // A flag for showing the categories field const shouldShowCategories = isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories)); + const shouldShowAmount = !props.receiptPath || props.isReadOnly; + // Fetches the first tag list of the policy const policyTag = PolicyUtils.getTag(props.policyTags); const policyTagList = lodashGet(policyTag, 'tags', {}); @@ -490,13 +492,14 @@ function MoneyRequestConfirmationList(props) { )} - {receiptImage || receiptThumbnail ? ( + {(receiptImage || receiptThumbnail) && ( - ) : ( + )} + {shouldShowAmount && ( - {!props.receiptPath && ( + {(!props.receiptPath || props.isReadOnly) && ( )} - {!props.isDistanceRequest && !props.receiptPath && ( + {(!props.isDistanceRequest || props.isReadOnly) && ( Date: Sat, 7 Oct 2023 03:50:55 +0800 Subject: [PATCH 21/35] Show SmartScan fields in ReadOnly split details page --- src/components/MoneyRequestConfirmationList.js | 14 ++++++++++---- src/pages/iou/SplitBillDetailsPage.js | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 7f1bacc1544d..3f491da5f3d9 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -141,6 +141,9 @@ const propTypes = { /** Whether the money request is a distance request */ isDistanceRequest: PropTypes.bool, + /** Whether the receipt associated with this report is being scanned */ + isScanning: PropTypes.bool, + /* Onyx Props */ /** Collection of categories attached to a policy */ policyCategories: PropTypes.objectOf(categoryPropTypes), @@ -176,6 +179,7 @@ const defaultProps = { transaction: {}, mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'}, isDistanceRequest: false, + isScanning: false, }; function MoneyRequestConfirmationList(props) { @@ -199,7 +203,9 @@ function MoneyRequestConfirmationList(props) { // A flag for showing the categories field const shouldShowCategories = isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories)); - const shouldShowAmount = !props.receiptPath || props.isReadOnly; + // A flag for showing SS fields: date, merchant, and amount, only when we don't have a receiptPath (e.g. manual request) + // or in the split details page when it's ReadOnly + const shouldShowSmartScanFields = (!props.receiptPath || props.isReadOnly) && !props.isScanning; // Fetches the first tag list of the policy const policyTag = PolicyUtils.getTag(props.policyTags); @@ -499,7 +505,7 @@ function MoneyRequestConfirmationList(props) { isAuthTokenRequired={!_.isEmpty(receiptThumbnail)} /> )} - {shouldShowAmount && ( + {shouldShowSmartScanFields && ( - {(!props.receiptPath || props.isReadOnly) && ( + {shouldShowSmartScanFields && ( )} - {(!props.isDistanceRequest || props.isReadOnly) && ( + {shouldShowSmartScanFields && ( )} From 9b2b9427734d1cc8cf55b31df248d08b6bf18032 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Sat, 7 Oct 2023 04:02:10 +0800 Subject: [PATCH 22/35] Rename receiptSource to receiptFilename --- src/components/MoneyRequestConfirmationList.js | 8 ++++---- src/libs/actions/IOU.js | 8 ++++---- src/pages/iou/SplitBillDetailsPage.js | 2 +- src/pages/iou/steps/MoneyRequestConfirmPage.js | 10 +++++----- src/types/onyx/IOU.ts | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 3f491da5f3d9..fbccbf7a915d 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -114,8 +114,8 @@ const propTypes = { /** File path of the receipt */ receiptPath: PropTypes.string, - /** File source of the receipt */ - receiptSource: PropTypes.string, + /** File name of the receipt */ + receiptFilename: PropTypes.string, /** List styles for OptionsSelector */ listStyles: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), @@ -171,7 +171,7 @@ const defaultProps = { reportID: '', ...withCurrentUserPersonalDetailsDefaultProps, receiptPath: '', - receiptSource: '', + receiptFilename: '', listStyles: [], policyCategories: {}, policyTags: {}, @@ -472,7 +472,7 @@ function MoneyRequestConfirmationList(props) { ); }, [confirm, props.bankAccountRoute, props.iouCurrencyCode, props.iouType, props.isReadOnly, props.policyID, selectedParticipants, splitOrRequestOptions, translate, formError]); - const {image: receiptImage, thumbnail: receiptThumbnail} = props.receiptPath && ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptSource); + const {image: receiptImage, thumbnail: receiptThumbnail} = props.receiptPath && ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptFilename); return ( diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js index 9e55bb21e36a..2c8f1adb1b11 100644 --- a/src/pages/iou/steps/MoneyRequestConfirmPage.js +++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js @@ -191,7 +191,7 @@ function MoneyRequestConfirmPage(props) { // If we have a receipt let's start the split bill by creating only the action, the transaction, and the group DM if needed if (iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT && props.iou.receiptPath) { const existingSplitChatReportID = CONST.REGEX.NUMBER.test(reportID.current) ? reportID.current : ''; - FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptSource).then((receipt) => { + FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptFilename).then((receipt) => { IOU.startSplitBill( selectedParticipants, props.currentUserPersonalDetails.login, @@ -232,8 +232,8 @@ function MoneyRequestConfirmPage(props) { return; } - if (props.iou.receiptPath && props.iou.receiptSource) { - FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptSource).then((file) => { + if (props.iou.receiptPath && props.iou.receiptFilename) { + FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptFilename).then((file) => { const receipt = file; receipt.state = file && isManualRequestDM ? CONST.IOU.RECEIPT_STATE.OPEN : CONST.IOU.RECEIPT_STATE.SCANREADY; requestMoney(selectedParticipants, trimmedComment, receipt); @@ -255,7 +255,7 @@ function MoneyRequestConfirmPage(props) { props.currentUserPersonalDetails.accountID, props.iou.currency, props.iou.receiptPath, - props.iou.receiptSource, + props.iou.receiptFilename, isDistanceRequest, requestMoney, createDistanceRequest, @@ -351,7 +351,7 @@ function MoneyRequestConfirmPage(props) { IOU.setMoneyRequestParticipants(newParticipants); }} receiptPath={props.iou.receiptPath} - receiptSource={props.iou.receiptSource} + receiptFilename={props.iou.receiptFilename} iouType={iouType.current} reportID={reportID.current} // The participants can only be modified when the action is initiated from directly within a group chat and not the floating-action-button. diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 7151bb84d1f1..a89b0d4530ef 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -16,7 +16,7 @@ type IOU = { merchant?: string; created?: string; receiptPath?: string; - receiptSource?: string; + receiptFilename?: string; transactionID?: string; participants?: Participant[]; tag?: string; From 424132f1b0b11dd743fae3a5d580435a751d394b Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Sat, 7 Oct 2023 04:07:34 +0800 Subject: [PATCH 23/35] Add null check --- src/components/MoneyRequestConfirmationList.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index fbccbf7a915d..6fcfc3bb4f08 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -472,7 +472,8 @@ function MoneyRequestConfirmationList(props) { ); }, [confirm, props.bankAccountRoute, props.iouCurrencyCode, props.iouType, props.isReadOnly, props.policyID, selectedParticipants, splitOrRequestOptions, translate, formError]); - const {image: receiptImage, thumbnail: receiptThumbnail} = props.receiptPath && ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptFilename); + const {image: receiptImage, thumbnail: receiptThumbnail} = + props.receiptPath && props.receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptFilename) : {}; return ( Date: Sun, 8 Oct 2023 19:33:50 +0800 Subject: [PATCH 24/35] Bug fix --- src/components/ReportActionItem/ReportPreview.js | 2 ++ src/libs/actions/IOU.js | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 401e70e2e3ac..b2b4cc27e144 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -105,6 +105,7 @@ const defaultProps = { }; function ReportPreview(props) { + console.log(props.iouReport); const managerID = props.iouReport.managerID || 0; const isCurrentUserManager = managerID === lodashGet(props.session, 'accountID'); const reportTotal = ReportUtils.getMoneyRequestTotal(props.iouReport); @@ -158,6 +159,7 @@ function ReportPreview(props) { return props.translate('iou.managerApproved', {manager: ReportUtils.getDisplayNameForParticipant(managerID, true)}); } const managerName = ReportUtils.isPolicyExpenseChat(props.chatReport) ? ReportUtils.getPolicyName(props.chatReport) : ReportUtils.getDisplayNameForParticipant(managerID, true); + console.log('manager name', managerID); return props.translate(iouSettled || props.iouReport.isWaitingOnBankAccount ? 'iou.payerPaid' : 'iou.payerOwes', {payer: managerName}); }; diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 3f279ac57f6f..26a0304ba069 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1328,7 +1328,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co onyxMethod: existingSplitChatReport ? Onyx.METHOD.MERGE : Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { - ...(!existingSplitChatReport ? {} : {[splitChatCreatedReportAction.reportActionID]: splitChatCreatedReportAction}), + ...(existingSplitChatReport ? {} : {[splitChatCreatedReportAction.reportActionID]: splitChatCreatedReportAction}), [splitIOUReportAction.reportActionID]: splitIOUReportAction, }, }, @@ -1344,7 +1344,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { - ...(!existingSplitChatReport ? {} : {[splitChatCreatedReportAction.reportActionID]: {pendingAction: null}}), + ...(existingSplitChatReport ? {} : {[splitChatCreatedReportAction.reportActionID]: {pendingAction: null}}), [splitIOUReportAction.reportActionID]: {pendingAction: null}, }, }, @@ -1457,7 +1457,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co receipt, comment, isFromGroupDM: !existingSplitChatReport, - ...(!existingSplitChatReport ? {} : {createdReportActionID: splitChatCreatedReportAction.reportActionID}), + ...(existingSplitChatReport ? {} : {createdReportActionID: splitChatCreatedReportAction.reportActionID}), }, {optimisticData, successData, failureData}, ); From ceb18726dcee39cf17cd4d48b128e095ca1dd0e0 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 00:11:55 +0800 Subject: [PATCH 25/35] Apply suggestions from code review Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/components/MoneyRequestConfirmationList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 6fcfc3bb4f08..49b639daf18b 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -203,8 +203,8 @@ function MoneyRequestConfirmationList(props) { // A flag for showing the categories field const shouldShowCategories = isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories)); - // A flag for showing SS fields: date, merchant, and amount, only when we don't have a receiptPath (e.g. manual request) - // or in the split details page when it's ReadOnly + // A flag for showing SmartScan fields: date, merchant, and amount, only when we don't have a receiptPath (e.g. manual request) + // or in the split details page which is ReadOnly const shouldShowSmartScanFields = (!props.receiptPath || props.isReadOnly) && !props.isScanning; // Fetches the first tag list of the policy From 5c47140e8a0dda0a3e041c3f479218511e7271ef Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 00:29:17 +0800 Subject: [PATCH 26/35] Hide showMore button when there are no more fields to display --- src/components/MoneyRequestConfirmationList.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index b518fadad676..caa6a597e98d 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -192,9 +192,6 @@ function MoneyRequestConfirmationList(props) { const {onSendMoney, onConfirm, onSelectParticipant, transaction} = props; const {translate, toLocaleDigit} = useLocalize(); - // A flag and a toggler for showing the rest of the form fields - const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); - const shouldShowAllFields = props.isDistanceRequest || shouldExpandFields; const isTypeRequest = props.iouType === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST; const {unit, rate, currency} = props.mileageRate; @@ -208,6 +205,10 @@ function MoneyRequestConfirmationList(props) { // or in the split details page which is ReadOnly const shouldShowSmartScanFields = (!props.receiptPath || props.isReadOnly) && !props.isScanning; + // A flag and a toggler for showing the rest of the form fields + const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); + const shouldShowAllFields = props.isDistanceRequest || shouldExpandFields || !shouldShowSmartScanFields; + // Fetches the first tag list of the policy const policyTag = PolicyUtils.getTag(props.policyTags); const policyTagList = lodashGet(policyTag, 'tags', {}); From e6de6c5c8cb9be3902dc2949bfd6bb3cfbbcf267 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 01:21:03 +0800 Subject: [PATCH 27/35] Fix navigation to split chat report --- src/libs/actions/IOU.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 36de8c4a25e3..d9f75a1b5fce 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1473,11 +1473,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co ); resetMoneyRequestInfo(); - if (existingSplitChatReport) { - Navigation.dismissModal(); - } else { - Navigation.dismissModal(splitChatReport.reportID); - } + Navigation.dismissModal(splitChatReport.reportID); Report.notifyNewAction(splitChatReport.chatReportID, currentUserAccountID); } From a5d29bac4f4cfabf205d7d2e15944a16af1d170e Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 13:49:25 +0800 Subject: [PATCH 28/35] Remove unnecessary param --- src/components/MoneyRequestConfirmationList.js | 6 ++---- src/libs/OptionsListUtils.js | 10 ++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 773bf6d77c34..a88252798101 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -256,8 +256,7 @@ function MoneyRequestConfirmationList(props) { const iouAmount = IOUUtils.calculateAmount(participantsList.length, props.iouAmount, props.iouCurrencyCode); return OptionsListUtils.getIOUConfirmationOptionsFromParticipants( participantsList, - CurrencyUtils.convertToDisplayString(iouAmount, props.iouCurrencyCode), - props.iouAmount === 0, + props.iouAmount > 0 ? CurrencyUtils.convertToDisplayString(iouAmount, props.iouCurrencyCode) : '', ); }, [props.iouAmount, props.iouCurrencyCode], @@ -305,8 +304,7 @@ function MoneyRequestConfirmationList(props) { const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, props.iouAmount, props.iouCurrencyCode, true); const formattedPayeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( payeePersonalDetails, - CurrencyUtils.convertToDisplayString(myIOUAmount, props.iouCurrencyCode), - props.iouAmount === 0, + props.iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, props.iouCurrencyCode) : '', ); sections.push( diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 601d68aceea9..82285545b303 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1300,10 +1300,9 @@ function getSearchOptions(reports, personalDetails, searchValue = '', betas) { * * @param {Object} personalDetail * @param {String} amountText - * @param {Boolean} shouldHideDescriptiveText * @returns {Object} */ -function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amountText, shouldHideDescriptiveText) { +function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amountText) { const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail.login); return { text: personalDetail.displayName || formattedLogin, @@ -1316,7 +1315,7 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amount id: personalDetail.accountID, }, ], - descriptiveText: shouldHideDescriptiveText ? '' : amountText, + descriptiveText: amountText, login: personalDetail.login, accountID: personalDetail.accountID, }; @@ -1327,13 +1326,12 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amount * * @param {Array} participants * @param {String} amountText - * @param {Boolean} shouldHideDescriptiveText * @returns {Array} */ -function getIOUConfirmationOptionsFromParticipants(participants, amountText, shouldHideDescriptiveText) { +function getIOUConfirmationOptionsFromParticipants(participants, amountText) { return _.map(participants, (participant) => ({ ...participant, - descriptiveText: shouldHideDescriptiveText ? '' : amountText, + descriptiveText: amountText, })); } From 92c9a42c917909690bf306c4bf6890f17fe873ce Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 13:58:04 +0800 Subject: [PATCH 29/35] Reverse changes in TransactionUtils --- src/libs/TransactionUtils.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 31bf90a6051f..beb1f9c323d6 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -220,10 +220,7 @@ function getCurrency(transaction: Transaction): string { * Return the merchant field from the transaction, return the modifiedMerchant if present. */ function getMerchant(transaction: Transaction): string { - if (transaction?.modifiedMerchant?.length) { - return transaction.modifiedMerchant; - } - return transaction?.merchant ?? ''; + return transaction?.modifiedMerchant ? transaction.modifiedMerchant : transaction?.merchant || ''; } /** @@ -265,12 +262,7 @@ function getTag(transaction: Transaction): string { * Return the created field from the transaction, return the modifiedCreated if present. */ function getCreated(transaction: Transaction): string { - let created: string; - if (transaction?.modifiedCreated?.length) { - created = transaction.modifiedCreated; - } else { - created = transaction?.created ?? ''; - } + const created = transaction?.modifiedCreated ? transaction.modifiedCreated : transaction?.created || ''; const createdDate = parseISO(created); if (isValid(createdDate)) { return format(createdDate, CONST.DATE.FNS_FORMAT_STRING); From 6c8f5743f11d3be26ea15a9914b12d04e907f731 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 14:00:06 +0800 Subject: [PATCH 30/35] Apply suggestions from code review Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/libs/actions/IOU.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d9f75a1b5fce..c5ed214661e4 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1268,7 +1268,7 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou } /** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned - * or user enters details manually. + * or user enters details manually. * * @param {Array} participants * @param {String} currentUserLogin @@ -1285,7 +1285,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co ? allReports[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); - const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; + const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat ?? false; const receiptObject = {}; let filename; @@ -1295,7 +1295,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co filename = receipt.name; } - // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 + // ReportID is -2 (aka "deleted") on the group transaction const splitTransaction = TransactionUtils.buildOptimisticTransaction(0, CONST.CURRENCY.USD, CONST.REPORT.SPLIT_REPORTID, comment, '', '', '', '', receiptObject, filename); // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat @@ -1444,7 +1444,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co accountID, avatar: UserUtils.getDefaultAvatarURL(accountID), displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), - login: participant.login, + login: participant.login || participant.text, isOptimisticPersonalDetail: true, }, }, From 5f7656baf3b8d745b98c364ac657de251e004d40 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 14:01:46 +0800 Subject: [PATCH 31/35] Tidy up code --- src/libs/actions/IOU.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d9f75a1b5fce..bb14c2b007de 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1287,13 +1287,8 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; - const receiptObject = {}; - let filename; - if (receipt && receipt.source) { - receiptObject.source = receipt.source; - receiptObject.state = receipt.state || CONST.IOU.RECEIPT_STATE.SCANREADY; - filename = receipt.name; - } + const {name: filename, source, state = CONST.IOU.RECEIPT_STATE.SCANREADY} = receipt || {}; + const receiptObject = {state, source}; // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 const splitTransaction = TransactionUtils.buildOptimisticTransaction(0, CONST.CURRENCY.USD, CONST.REPORT.SPLIT_REPORTID, comment, '', '', '', '', receiptObject, filename); From 5527d4cfbef8dc1d1349009207f9ccc53f8435bd Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 14:13:22 +0800 Subject: [PATCH 32/35] Add error to split chat created action on failure --- src/libs/actions/IOU.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index bb14c2b007de..7c659e5d8727 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1287,7 +1287,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; - const {name: filename, source, state = CONST.IOU.RECEIPT_STATE.SCANREADY} = receipt || {}; + const {name: filename, source, state = CONST.IOU.RECEIPT_STATE.SCANREADY} = receipt; const receiptObject = {state, source}; // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 @@ -1403,9 +1403,12 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { - [splitIOUReportAction.reportActionID]: { + [splitChatCreatedReportAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), }, + [splitIOUReportAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError('report.genericCreateFailureMessage'), + }, }, }, ); From 25f0a2a0fbf61479ab8f8687e6450df4ddad922e Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 14:22:10 +0800 Subject: [PATCH 33/35] fix lint --- src/libs/actions/IOU.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 48c63b029f81..33f2ee80bdde 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1285,7 +1285,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co ? allReports[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); - const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat ?? false; + const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat || false; const {name: filename, source, state = CONST.IOU.RECEIPT_STATE.SCANREADY} = receipt; const receiptObject = {state, source}; From ab6a1405566e65cebadfc371fd67f636fce1c290 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 18:09:36 +0800 Subject: [PATCH 34/35] Add comment --- src/components/MoneyRequestConfirmationList.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index a88252798101..acaa83181bbf 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -504,6 +504,9 @@ function MoneyRequestConfirmationList(props) { )} From 993f0ce32bbafcb7c66fe259769dc411f0726b2d Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Tue, 10 Oct 2023 19:48:48 +0800 Subject: [PATCH 35/35] Save splits optimistically in the transaction comment --- src/libs/actions/IOU.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 33f2ee80bdde..8d9fb64defa2 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1455,6 +1455,17 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co }); }); + // Save the new splits array into the transaction's comment in case the user calls CompleteSplitBill while offline + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, + value: { + comment: { + splits, + }, + }, + }); + API.write( 'StartSplitBill', {