From acfe83353708b96475510eedd1f58380c153492b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 9 May 2024 14:30:44 +0200 Subject: [PATCH 1/6] Enable invoice creation from invoice room --- src/components/MoneyRequestConfirmationList.tsx | 2 +- src/libs/ReportUtils.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 2c592c20f4c6..1bbe433dc029 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -1123,7 +1123,7 @@ function MoneyRequestConfirmationList({ style={styles.moneyRequestMenuItem} labelStyle={styles.mt2} titleStyle={styles.flex1} - disabled={didConfirm || !canUpdateSenderWorkspace} + disabled={didConfirm} /> )} {!isDistanceRequest && diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5749e241b6fb..aa317ecd149f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5447,16 +5447,21 @@ function isGroupChatAdmin(report: OnyxEntry, accountID: number) { * - Self DMs * - own policy expense chats * - open and processing expense reports tied to own policy expense chat - * + * - Send invoice option should show for: + * - invoice rooms if the user is an admin of the sender workspace * None of the options should show in chat threads or if there is some special Expensify account * as a participant of the report. */ function getMoneyRequestOptions(report: OnyxEntry, policy: OnyxEntry, reportParticipants: number[], canUseTrackExpense = true, filterDeprecatedTypes = false): IOUType[] { // In any thread or task report, we do not allow any new expenses yet - if (isChatThread(report) || isTaskReport(report) || (!canUseTrackExpense && isSelfDM(report)) || isInvoiceRoom(report) || isInvoiceReport(report)) { + if (isChatThread(report) || isTaskReport(report) || (!canUseTrackExpense && isSelfDM(report)) || isInvoiceReport(report)) { return []; } + if (isInvoiceRoom(report) && isPolicyAdmin(report?.policyID ?? '', allPolicies)) { + return [CONST.IOU.TYPE.INVOICE]; + } + // We don't allow IOU actions if an Expensify account is a participant of the report, unless the policy that the report is on is owned by an Expensify account const doParticipantsIncludeExpensifyAccounts = lodashIntersection(reportParticipants, CONST.EXPENSIFY_ACCOUNT_IDS).length > 0; const isPolicyOwnedByExpensifyAccounts = report?.policyID ? CONST.EXPENSIFY_ACCOUNT_IDS.includes(getPolicy(report?.policyID ?? '')?.ownerAccountID ?? 0) : false; From c72355ee4d6b8f2b05c28da596452cc05d0debde Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 9 May 2024 14:54:15 +0200 Subject: [PATCH 2/6] Fix options display for invoice receiver --- src/libs/ReportUtils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index aa317ecd149f..74f10a87cf8d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5458,8 +5458,11 @@ function getMoneyRequestOptions(report: OnyxEntry, policy: OnyxEntry Date: Thu, 9 May 2024 15:36:03 +0200 Subject: [PATCH 3/6] Fix invoice receivers preview --- src/libs/actions/IOU.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index eb7b82800358..7bebd109c424 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6377,19 +6377,17 @@ function setMoneyRequestParticipantsFromReport(transactionID: string, report: On if (ReportUtils.isPolicyExpenseChat(chatReport) || shouldAddAsReport) { participants = [{accountID: 0, reportID: chatReport?.reportID, isPolicyExpenseChat: ReportUtils.isPolicyExpenseChat(chatReport), selected: true}]; + } else if (ReportUtils.isInvoiceRoom(chatReport)) { + participants = [ + {...chatReport?.invoiceReceiver, selected: true}, + { + policyID: chatReport?.policyID, + isSender: true, + selected: false, + }, + ]; } else { participants = (chatReport?.participantAccountIDs ?? []).filter((accountID) => currentUserAccountID !== accountID).map((accountID) => ({accountID, selected: true})); - - if (ReportUtils.isInvoiceRoom(chatReport)) { - participants = [ - ...participants, - { - policyID: chatReport?.policyID, - isSender: true, - selected: false, - }, - ]; - } } Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants, participantsAutoAssigned: true}); From ac74e8d7927a4608b122696052ef68ce7d6805b7 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 10 May 2024 09:26:06 +0200 Subject: [PATCH 4/6] Display chat as a receiver if user send invoice from existing room --- src/libs/OptionsListUtils.ts | 4 ++++ src/libs/ReportUtils.ts | 1 + src/libs/actions/IOU.ts | 11 ++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 082952e58f9e..522cd533614e 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -696,6 +696,7 @@ function createOption( result.isDefaultRoom = ReportUtils.isDefaultRoom(report); result.isArchivedRoom = ReportUtils.isArchivedRoom(report); result.isExpenseReport = ReportUtils.isExpenseReport(report); + result.isInvoiceRoom = ReportUtils.isInvoiceRoom(report); result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); result.isThread = ReportUtils.isChatThread(report); result.isTaskReport = ReportUtils.isTaskReport(report); @@ -792,6 +793,9 @@ function getReportOption(participant: Participant): ReportUtils.OptionData { // Update text & alternateText because createOption returns workspace name only if report is owned by the user if (option.isSelfDM) { option.alternateText = Localize.translateLocal('reportActionsView.yourSpace'); + } else if (option.isInvoiceRoom) { + option.text = ReportUtils.getReportName(report); + option.alternateText = Localize.translateLocal('workspace.common.invoices'); } else { option.text = ReportUtils.getPolicyName(report); option.alternateText = Localize.translateLocal('workspace.common.workspace'); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 74f10a87cf8d..92d37cfbbf5e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -434,6 +434,7 @@ type OptionData = { parentReportAction?: OnyxEntry; displayNamesWithTooltips?: DisplayNameWithTooltips | null; isDefaultRoom?: boolean; + isInvoiceRoom?: boolean; isExpenseReport?: boolean; isOptimisticPersonalDetail?: boolean; selected?: boolean; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7bebd109c424..b5f2488772cb 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1609,8 +1609,8 @@ function getSendInvoiceInformation( const {amount = 0, currency = '', created = '', merchant = '', category = '', tag = '', billable, comment, participants} = transaction ?? {}; const trimmedComment = (comment?.comment ?? '').trim(); const senderWorkspaceID = participants?.find((participant) => participant?.isSender)?.policyID ?? ''; - const receiverParticipant = participants?.find((participant) => participant?.accountID); - const receiverAccountID = receiverParticipant?.accountID ?? -1; + const receiverParticipant = participants?.find((participant) => participant?.accountID) ?? invoiceChatReport?.invoiceReceiver; + const receiverAccountID = receiverParticipant && 'accountID' in receiverParticipant && receiverParticipant.accountID ? receiverParticipant.accountID : -1; let receiver = ReportUtils.getPersonalDetailsForAccountID(receiverAccountID); let optimisticPersonalDetailListAction = {}; @@ -1661,11 +1661,12 @@ function getSendInvoiceInformation( // STEP 4: Add optimistic personal details for participant const shouldCreateOptimisticPersonalDetails = isNewChatReport && !allPersonalDetails[receiverAccountID]; if (shouldCreateOptimisticPersonalDetails) { + const receiverLogin = receiverParticipant && 'login' in receiverParticipant && receiverParticipant.login ? receiverParticipant.login : ''; receiver = { accountID: receiverAccountID, avatar: UserUtils.getDefaultAvatarURL(receiverAccountID), - displayName: LocalePhoneNumber.formatPhoneNumber(receiverParticipant?.login ?? ''), - login: receiverParticipant?.login, + displayName: LocalePhoneNumber.formatPhoneNumber(receiverLogin), + login: receiverLogin, isOptimisticPersonalDetail: true, }; @@ -6379,7 +6380,7 @@ function setMoneyRequestParticipantsFromReport(transactionID: string, report: On participants = [{accountID: 0, reportID: chatReport?.reportID, isPolicyExpenseChat: ReportUtils.isPolicyExpenseChat(chatReport), selected: true}]; } else if (ReportUtils.isInvoiceRoom(chatReport)) { participants = [ - {...chatReport?.invoiceReceiver, selected: true}, + {reportID: chatReport?.reportID, selected: true}, { policyID: chatReport?.policyID, isSender: true, From 6135702093ac165fb3e864e0b8970e1c720107ed Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 10 May 2024 17:14:19 +0200 Subject: [PATCH 5/6] Update send invoice icon to match global create --- .../ReportActionCompose/AttachmentPickerWithMenuItems.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 07de62b1eabd..5759ca84945e 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -149,7 +149,7 @@ function AttachmentPickerWithMenuItems({ onSelected: () => IOU.startMoneyRequest(CONST.IOU.TYPE.TRACK, report?.reportID ?? ''), }, [CONST.IOU.TYPE.INVOICE]: { - icon: Expensicons.Invoice, + icon: Expensicons.InvoiceGeneric, text: translate('workspace.invoices.sendInvoice'), onSelected: () => IOU.startMoneyRequest(CONST.IOU.TYPE.INVOICE, report?.reportID ?? ''), }, From ac13964975668c8153ab97e01dc6a4b1b25ded0e Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 13 May 2024 15:06:59 +0200 Subject: [PATCH 6/6] Enable invoice room option on the confirmation screen --- src/components/MoneyRequestConfirmationList.tsx | 2 +- src/types/onyx/IOU.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 7610967d63db..04147996e9a2 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -599,7 +599,7 @@ function MoneyRequestConfirmationList({ const formattedSelectedParticipants = selectedParticipants.map((participant) => ({ ...participant, isSelected: false, - isDisabled: !participant.isPolicyExpenseChat && !participant.isSelfDM && ReportUtils.isOptimisticPersonalDetail(participant.accountID ?? -1), + isDisabled: !participant.isInvoiceRoom && !participant.isPolicyExpenseChat && !participant.isSelfDM && ReportUtils.isOptimisticPersonalDetail(participant.accountID ?? -1), })); options.push({ title: translate('common.to'), diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 82bf4d6efc04..8b0ca935f4b3 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -7,6 +7,7 @@ type Participant = { login?: string; displayName?: string; isPolicyExpenseChat?: boolean; + isInvoiceRoom?: boolean; isOwnPolicyExpenseChat?: boolean; chatType?: ValueOf; reportID?: string;