diff --git a/src/CONST.ts b/src/CONST.ts index 8abd4c087b16..125eedd4257a 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1380,9 +1380,9 @@ const CONST = { OWNER_EMAIL_FAKE: '_FAKE_', OWNER_ACCOUNT_ID_FAKE: 0, REIMBURSEMENT_CHOICES: { - REIMBURSEMENT_YES: 'reimburseYes', - REIMBURSEMENT_NO: 'reimburseNo', - REIMBURSEMENT_MANUAL: 'reimburseManual', + REIMBURSEMENT_YES: 'reimburseYes', // Direct + REIMBURSEMENT_NO: 'reimburseNo', // None + REIMBURSEMENT_MANUAL: 'reimburseManual', // Indirect }, ID_FAKE: '_FAKE_', EMPTY: 'EMPTY', diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 3f551da788f5..102f85ea49b9 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -55,14 +55,10 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport); const policyType = policy?.type; - const isPolicyAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && policy?.role === CONST.POLICY.ROLE.ADMIN; const isAutoReimbursable = ReportUtils.canBeAutoReimbursed(moneyRequestReport, policy); const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicy(moneyRequestReport); const isManager = ReportUtils.isMoneyRequestReport(moneyRequestReport) && session?.accountID === moneyRequestReport.managerID; - const isPayer = isPaidGroupPolicy - ? // In a group policy, the admin approver can pay the report directly by skipping the approval step - isPolicyAdmin && (isApproved || isManager) - : isPolicyAdmin || (ReportUtils.isMoneyRequestReport(moneyRequestReport) && isManager); + const isPayer = ReportUtils.isPayer(session, moneyRequestReport); const isDraft = ReportUtils.isDraftExpenseReport(moneyRequestReport); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index cbc728ffd1ce..ad5dd0ba9caa 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -120,7 +120,6 @@ function ReportPreview({ const managerID = iouReport?.managerID ?? 0; const isCurrentUserManager = managerID === session?.accountID; const {totalDisplaySpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport); - const policyType = policy?.type; const isAutoReimbursable = ReportUtils.canBeAutoReimbursed(iouReport, policy); const iouSettled = ReportUtils.isSettled(iouReportID); @@ -132,7 +131,6 @@ function ReportPreview({ const isApproved = ReportUtils.isReportApproved(iouReport); const canAllowSettlement = ReportUtils.hasUpdatedTotal(iouReport); - const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(iouReport); const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(iouReportID); const numberOfScanningReceipts = transactionsWithReceipts.filter((transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length; @@ -209,11 +207,7 @@ function ReportPreview({ const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); - const isPolicyAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && policy?.role === CONST.POLICY.ROLE.ADMIN; - const isPayer = isPaidGroupPolicy - ? // In a paid group policy, the admin approver can pay the report directly by skipping the approval step - isPolicyAdmin && (isApproved || isCurrentUserManager) - : isPolicyAdmin || (isMoneyRequestReport && isCurrentUserManager); + const isPayer = ReportUtils.isPayer(session, iouReport); const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy); const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); const shouldShowPayButton = useMemo( diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 50bfcd4cc8be..d1c5a9450902 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -21,6 +21,7 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import * as Expensicons from './Icon/Expensicons'; import KYCWall from './KYCWall'; +import {useSession} from './OnyxProvider'; type KYCFlowEvent = GestureResponderEvent | KeyboardEvent | undefined; @@ -133,6 +134,13 @@ function SettlementButton({ PaymentMethods.openWalletPage(); }, []); + const policy = ReportUtils.getPolicy(policyID); + const session = useSession(); + const chatReport = ReportUtils.getReport(chatReportID); + const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport as OnyxEntry); + const shouldShowPaywithExpensifyOption = + !isPaidGroupPolicy || + (!shouldHidePaymentOptions && policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES && policy?.reimburserEmail === session?.email); const paymentButtonOptions = useMemo(() => { const buttonOptions = []; const isExpenseReport = ReportUtils.isExpenseReport(iouReport); @@ -172,7 +180,7 @@ function SettlementButton({ if (canUseWallet) { buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]); } - if (isExpenseReport) { + if (isExpenseReport && shouldShowPaywithExpensifyOption) { buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.VBBA]); } buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]); @@ -189,7 +197,6 @@ function SettlementButton({ // We don't want to reorder the options when the preferred payment method changes while the button is still visible // eslint-disable-next-line react-hooks/exhaustive-deps }, [currency, formattedAmount, iouReport, policyID, translate, shouldHidePaymentOptions, shouldShowApproveButton]); - const selectPaymentType = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType, triggerKYCFlow: TriggerKYCFlow) => { if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) { triggerKYCFlow(event, iouPaymentType); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0f8656adfa51..5ffcf55339a2 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1221,6 +1221,29 @@ function isOneOnOneChat(report: OnyxEntry): boolean { ); } +/** + * Checks if the current user is a payer of the request + */ + +function isPayer(session: OnyxEntry, iouReport: OnyxEntry) { + const isApproved = isReportApproved(iouReport); + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${iouReport?.policyID}`] ?? null; + const policyType = policy?.type; + const isAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && policy?.role === CONST.POLICY.ROLE.ADMIN; + const isManager = iouReport?.managerID === session?.accountID; + if (isPaidGroupPolicy(iouReport)) { + if (policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES) { + const isReimburser = session?.email === policy?.reimburserEmail; + return isReimburser && (isApproved || isManager); + } + if (policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL) { + return isAdmin && (isApproved || isManager); + } + return false; + } + return isAdmin || (isMoneyRequestReport(iouReport) && isManager); +} + /** * Get the notification preference given a report */ @@ -1603,9 +1626,9 @@ function getIcons( name: personalDetails?.[report?.ownerAccountID ?? -1]?.displayName ?? '', fallbackIcon: personalDetails?.[report?.ownerAccountID ?? -1]?.fallbackIcon, }; - const isPayer = currentUserAccountID === report?.managerID; + const isManager = currentUserAccountID === report?.managerID; - return isPayer ? [managerIcon, ownerIcon] : [ownerIcon, managerIcon]; + return isManager ? [managerIcon, ownerIcon] : [ownerIcon, managerIcon]; } return getIconsForParticipants(report?.participantAccountIDs ?? [], personalDetails); @@ -5156,6 +5179,7 @@ export { hasSingleParticipant, getReportRecipientAccountIDs, isOneOnOneChat, + isPayer, goBackToDetailsPage, getTransactionReportName, getTransactionDetails, diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 1662a76c02df..98a3139f807a 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -215,6 +215,9 @@ type Policy = OnyxCommon.OnyxValueWithOfflineFeedback< /** Collection of tax rates attached to a policy */ taxRates?: TaxRatesWithDefault; + /** Email of the reimburser when reimbursement is set direct */ + reimburserEmail?: string; + /** ReportID of the admins room for this workspace */ chatReportIDAdmins?: number;