diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 31e7792a7310..5784be21bac3 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -8,6 +8,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as HeaderUtils from '@libs/HeaderUtils'; import Navigation from '@libs/Navigation/Navigation'; +import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; @@ -50,9 +51,13 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const {translate} = useLocalize(); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(moneyRequestReport); + const isApproved = ReportUtils.isReportApproved(moneyRequestReport); const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport); const policyType = policy?.type; + const isAutoReimbursable = ReportUtils.canBeAutoReimbursed(moneyRequestReport, policy); + const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicy(moneyRequestReport); + const isManager = ReportUtils.isMoneyRequestReport(moneyRequestReport) && session?.accountID === moneyRequestReport.managerID; const isPayer = ReportUtils.isPayer(session, moneyRequestReport); const isDraft = ReportUtils.isDraftExpenseReport(moneyRequestReport); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false); @@ -65,12 +70,22 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money setIsConfirmModalVisible(false); }, [moneyRequestReport, chatReport]); - const shouldShowPayButton = useMemo(() => IOU.canIOUBePaid(moneyRequestReport, chatReport, policy), [moneyRequestReport, chatReport, policy]); - - const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(moneyRequestReport, chatReport, policy), [moneyRequestReport, chatReport, policy]); - + const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy); + const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); + const shouldShowPayButton = useMemo( + () => isPayer && !isDraft && !isSettled && !moneyRequestReport.isWaitingOnBankAccount && reimbursableSpend !== 0 && !ReportUtils.isArchivedRoom(chatReport) && !isAutoReimbursable, + [isPayer, isDraft, isSettled, moneyRequestReport, reimbursableSpend, chatReport, isAutoReimbursable], + ); + const shouldShowApproveButton = useMemo(() => { + if (!isPaidGroupPolicy) { + return false; + } + if (isOnInstantSubmitPolicy && isOnSubmitAndClosePolicy) { + return false; + } + return isManager && !isDraft && !isApproved && !isSettled; + }, [isPaidGroupPolicy, isManager, isDraft, isApproved, isSettled, isOnInstantSubmitPolicy, isOnSubmitAndClosePolicy]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = !ReportUtils.isClosedExpenseReportWithNoExpenses(moneyRequestReport) && isFromPaidPolicy && !!nextStep?.message?.length; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index c78a3cedebbd..381302489699 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -19,6 +19,7 @@ import ControlSelection from '@libs/ControlSelection'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Navigation from '@libs/Navigation/Navigation'; +import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportActionUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -29,7 +30,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Policy, Report, ReportAction, Transaction, TransactionViolations} from '@src/types/onyx'; +import type {Policy, Report, ReportAction, Session, Transaction, TransactionViolations} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import ReportActionItemImages from './ReportActionItemImages'; @@ -43,6 +44,9 @@ type ReportPreviewOnyxProps = { /** Active IOU Report for current report */ iouReport: OnyxEntry; + /** Session info for the currently logged in user. */ + session: OnyxEntry; + /** All the transactions, used to update ReportPreview label and status */ transactions: OnyxCollection; @@ -81,6 +85,7 @@ type ReportPreviewProps = ReportPreviewOnyxProps & { function ReportPreview({ iouReport, + session, policy, iouReportID, policyID, @@ -113,9 +118,12 @@ function ReportPreview({ ); const managerID = iouReport?.managerID ?? 0; + const isCurrentUserManager = managerID === session?.accountID; const {totalDisplaySpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport); + const isAutoReimbursable = ReportUtils.canBeAutoReimbursed(iouReport, policy); const iouSettled = ReportUtils.isSettled(iouReportID); + const iouCanceled = ReportUtils.isArchivedRoom(chatReport); const numberOfRequests = ReportActionUtils.getNumberOfMoneyRequests(action); const moneyRequestComment = action?.childLastMoneyRequestComment ?? ''; const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); @@ -200,10 +208,23 @@ function ReportPreview({ const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); - const shouldShowPayButton = useMemo(() => IOU.canIOUBePaid(iouReport, chatReport, policy), [iouReport, chatReport, policy]); - - const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(iouReport, chatReport, policy), [iouReport, chatReport, policy]); - + const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); + const isPayer = ReportUtils.isPayer(session, iouReport); + const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy); + const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); + const shouldShowPayButton = useMemo( + () => isPayer && !isDraftExpenseReport && !iouSettled && !iouReport?.isWaitingOnBankAccount && reimbursableSpend !== 0 && !iouCanceled && !isAutoReimbursable, + [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport], + ); + const shouldShowApproveButton = useMemo(() => { + if (!isPaidGroupPolicy) { + return false; + } + if (isOnInstantSubmitPolicy && isOnSubmitAndClosePolicy) { + return false; + } + return isCurrentUserManager && !isDraftExpenseReport && !isApproved && !iouSettled; + }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, isOnInstantSubmitPolicy, isOnSubmitAndClosePolicy, iouSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; /* @@ -332,6 +353,9 @@ export default withOnyx({ iouReport: { key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, }, + session: { + key: ONYXKEYS.SESSION, + }, transactions: { key: ONYXKEYS.COLLECTION.TRANSACTION, }, diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 9f5b0ada4cb1..f6534e075773 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -236,7 +236,7 @@ function isInstantSubmitEnabled(policy: OnyxEntry | EmptyObject): boolea /** * Checks if policy's approval mode is "optional", a.k.a. "Submit & Close" */ -function isSubmitAndClose(policy: OnyxEntry | EmptyObject): boolean { +function isSubmitAndClose(policy: OnyxEntry): boolean { return policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index afa7efe93938..69ed92689b6c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5171,8 +5171,8 @@ function getAllAncestorReportActionIDs(report: Report | null | undefined): Ances return allAncestorIDs; } -function canBeAutoReimbursed(report: OnyxEntry, policy: OnyxEntry | EmptyObject): boolean { - if (isEmptyObject(policy)) { +function canBeAutoReimbursed(report: OnyxEntry, policy: OnyxEntry = null): boolean { + if (!policy) { return false; } type CurrencyType = (typeof CONST.DIRECT_REIMBURSEMENT_CURRENCIES)[number]; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 1c0d7fef2729..cb3aa20ab6a7 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3677,73 +3677,10 @@ function sendMoneyWithWallet(report: OnyxTypes.Report, amount: number, currency: Report.notifyNewAction(params.chatReportID, managerID); } -function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry | EmptyObject, policy: OnyxEntry | EmptyObject) { - const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); - const iouCanceled = ReportUtils.isArchivedRoom(chatReport); - - if (isEmptyObject(iouReport)) { - return false; - } - - const isPayer = ReportUtils.isPayer( - { - email: currentUserEmail, - accountID: userAccountID, - }, - iouReport, - ); - - const isDraftExpenseReport = isPolicyExpenseChat && ReportUtils.isDraftExpenseReport(iouReport); - const iouSettled = ReportUtils.isSettled(iouReport?.reportID); - - const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport); - const isAutoReimbursable = ReportUtils.canBeAutoReimbursed(iouReport, policy); - return isPayer && !isDraftExpenseReport && !iouSettled && !iouReport?.isWaitingOnBankAccount && reimbursableSpend !== 0 && !iouCanceled && !isAutoReimbursable; -} - -function canApproveIOU(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry | EmptyObject, policy: OnyxEntry | EmptyObject) { - if (isEmptyObject(chatReport)) { - return false; - } - const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); - if (!isPaidGroupPolicy) { - return false; - } - - const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy); - const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); - if (isOnInstantSubmitPolicy && isOnSubmitAndClosePolicy) { - return false; - } - - const managerID = iouReport?.managerID ?? 0; - const isCurrentUserManager = managerID === userAccountID; - const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); - - const isDraftExpenseReport = isPolicyExpenseChat && ReportUtils.isDraftExpenseReport(iouReport); - const isApproved = ReportUtils.isReportApproved(iouReport); - const iouSettled = ReportUtils.isSettled(iouReport?.reportID); - - return isCurrentUserManager && !isDraftExpenseReport && !isApproved && !iouSettled; -} - -function hasIOUToApproveOrPay(chatReport: OnyxEntry | EmptyObject, excludedIOUReportID: string): boolean { - const chatReportActions = ReportActionsUtils.getAllReportActions(chatReport?.reportID ?? ''); - - return Object.values(chatReportActions).some((action) => { - const iouReport = ReportUtils.getReport(action.childReportID ?? ''); - const policy = ReportUtils.getPolicy(iouReport?.policyID); - - const shouldShowSettlementButton = canIOUBePaid(iouReport, chatReport, policy) || canApproveIOU(iouReport, chatReport, policy); - return action.childReportID?.toString() !== excludedIOUReportID && action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && shouldShowSettlementButton; - }); -} - function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject) { const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.APPROVED); - const chatReport = ReportUtils.getReport(expenseReport.chatReportID); const optimisticReportActionsData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, @@ -3766,21 +3703,12 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject) { statusNum: CONST.REPORT.STATUS_NUM.APPROVED, }, }; - - const optimisticChatReportData: OnyxUpdate = { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport?.chatReportID}`, - value: { - hasOutstandingChildRequest: hasIOUToApproveOrPay(chatReport, expenseReport?.reportID ?? ''), - }, - }; - const optimisticNextStepData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, value: optimisticNextStep, }; - const optimisticData: OnyxUpdate[] = [optimisticIOUReportData, optimisticReportActionsData, optimisticNextStepData, optimisticChatReportData]; + const optimisticData: OnyxUpdate[] = [optimisticIOUReportData, optimisticReportActionsData, optimisticNextStepData]; const successData: OnyxUpdate[] = [ { @@ -3804,13 +3732,6 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject) { }, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.chatReportID}`, - value: { - hasOutstandingChildRequest: chatReport?.hasOutstandingChildRequest, - }, - }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, @@ -4410,6 +4331,4 @@ export { cancelPayment, navigateToStartStepIfScanFileCannotBeRead, savePreferredPaymentMethod, - canIOUBePaid, - canApproveIOU, };