diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index ef2dee3fd01b..0bd9fcbb67b7 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -76,7 +76,7 @@ import type {ReportsSplitNavigatorParamList} from '@libs/Navigation/types'; import {getBankAccountLastFourDigits} from '@libs/PaymentUtils'; import Permissions from '@libs/Permissions'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; -import {getCleanedTagName, hasDynamicExternalWorkflow, isPolicyAdmin, isPolicyMember, isPolicyOwner} from '@libs/PolicyUtils'; +import {getCleanedTagName, isPolicyAdmin, isPolicyMember, isPolicyOwner} from '@libs/PolicyUtils'; import {containsActionableFollowUps, parseFollowupsFromHtml} from '@libs/ReportActionFollowupUtils'; import { extractLinksFromMessageHtml, @@ -170,8 +170,6 @@ import { getWorkspaceTagUpdateMessage, getWorkspaceTaxUpdateMessage, getWorkspaceUpdateFieldMessage, - hasPendingDEWApprove, - hasPendingDEWSubmit, isActionableAddPaymentCard, isActionableCardFraudAlert, isActionableJoinRequest, @@ -188,7 +186,6 @@ import { isDeletedAction, isDeletedParentAction as isDeletedParentActionUtils, isIOURequestReportAction, - isMarkAsClosedAction, isMessageDeleted, isMoneyRequestAction, isPendingRemove, @@ -250,6 +247,7 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import type {JoinWorkspaceResolution} from '@src/types/onyx/OriginalMessage'; import {isEmptyObject, isEmptyValueObject} from '@src/types/utils/EmptyObject'; +import ApprovalFlowContent, {isApprovalFlowAction} from './actionContents/ApprovalFlowContent'; import SimpleMessageContent, {isSimpleMessageAction} from './actionContents/SimpleMessageContent'; import {RestrictedReadOnlyContextMenuActions} from './ContextMenu/ContextMenuActions'; import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu'; @@ -1364,47 +1362,16 @@ function PureReportActionItem({ originalReport={originalReport} /> ); - } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) || isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) || isMarkAsClosedAction(action)) { - const wasSubmittedViaHarvesting = !isMarkAsClosedAction(action) ? (getOriginalMessage(action)?.harvesting ?? false) : false; - const isDEWPolicy = hasDynamicExternalWorkflow(policy); - - const isPendingAdd = action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD; - if (wasSubmittedViaHarvesting) { - children = ( - - ); - } else if (hasPendingDEWSubmit(reportMetadata, isDEWPolicy) && isPendingAdd) { - children = ; - } else if (isDEWPolicy) { - // Don't show a memo for DEW actions, it's shown in the Concierge action below - children = ; - } else { - children = ; - } - } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.APPROVED)) { - const wasAutoApproved = getOriginalMessage(action)?.automaticAction ?? false; - const isDEWPolicy = hasDynamicExternalWorkflow(policy); - const isPendingAdd = action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD; - - if (wasAutoApproved) { - children = ( - - ); - } else if (hasPendingDEWApprove(reportMetadata, isDEWPolicy) && isPendingAdd) { - children = ; - } else { - children = ; - } + } else if (isApprovalFlowAction(action)) { + children = ( + + ); } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.IOU) && getOriginalMessage(action)?.type === CONST.IOU.REPORT_ACTION_TYPE.PAY) { const wasAutoPaid = getOriginalMessage(action)?.automaticAction ?? false; const paymentType = getOriginalMessage(action)?.paymentType; @@ -1453,17 +1420,6 @@ function PureReportActionItem({ children = ; } else if (isSimpleMessageAction(action)) { children = ; - } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.FORWARDED)) { - const wasAutoForwarded = getOriginalMessage(action)?.automaticAction ?? false; - if (wasAutoForwarded) { - children = ( - - ${translate('iou.automaticallyForwarded')}`} /> - - ); - } else { - children = ; - } } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.CORPORATE_UPGRADE) { children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.CORPORATE_FORCE_UPGRADE) { diff --git a/src/pages/inbox/report/actionContents/ApprovalFlowContent.tsx b/src/pages/inbox/report/actionContents/ApprovalFlowContent.tsx new file mode 100644 index 000000000000..a711068df2b2 --- /dev/null +++ b/src/pages/inbox/report/actionContents/ApprovalFlowContent.tsx @@ -0,0 +1,102 @@ +import React from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import RenderHTML from '@components/RenderHTML'; +import useLocalize from '@hooks/useLocalize'; +import {hasDynamicExternalWorkflow} from '@libs/PolicyUtils'; +import {getOriginalMessage, hasPendingDEWApprove, hasPendingDEWSubmit, isActionOfType, isMarkAsClosedAction} from '@libs/ReportActionsUtils'; +import ReportActionItemBasicMessage from '@pages/inbox/report/ReportActionItemBasicMessage'; +import ReportActionItemMessageWithExplain from '@pages/inbox/report/ReportActionItemMessageWithExplain'; +import CONST from '@src/CONST'; +import type * as OnyxTypes from '@src/types/onyx'; + +type ApprovalFlowContentProps = { + action: OnyxTypes.ReportAction; + policy: OnyxEntry; + reportMetadata: OnyxEntry; + childReport: OnyxEntry; + originalReport: OnyxEntry; +}; + +function isApprovalFlowAction(action: OnyxTypes.ReportAction): boolean { + return ( + isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) || + isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) || + isMarkAsClosedAction(action) || + isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.APPROVED) || + isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.FORWARDED) + ); +} + +function ApprovalFlowContent({action, policy, reportMetadata, childReport, originalReport}: ApprovalFlowContentProps) { + const {translate} = useLocalize(); + const isDEWPolicy = hasDynamicExternalWorkflow(policy); + const isPendingAdd = action?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD; + + if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) || isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) || isMarkAsClosedAction(action)) { + const wasSubmittedViaHarvesting = !isMarkAsClosedAction(action) ? (getOriginalMessage(action)?.harvesting ?? false) : false; + + if (wasSubmittedViaHarvesting) { + return ( + + ); + } + + if (hasPendingDEWSubmit(reportMetadata, isDEWPolicy) && isPendingAdd) { + return ; + } + + if (isDEWPolicy) { + // Don't show a memo for DEW actions, it's shown in the Concierge action below + return ; + } + + return ; + } + + if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.APPROVED)) { + const wasAutoApproved = getOriginalMessage(action)?.automaticAction ?? false; + + if (wasAutoApproved) { + return ( + + ); + } + + if (hasPendingDEWApprove(reportMetadata, isDEWPolicy) && isPendingAdd) { + return ; + } + + return ; + } + + if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.FORWARDED)) { + const wasAutoForwarded = getOriginalMessage(action)?.automaticAction ?? false; + + if (wasAutoForwarded) { + return ( + + ${translate('iou.automaticallyForwarded')}`} /> + + ); + } + + return ; + } + + return null; +} + +ApprovalFlowContent.displayName = 'ApprovalFlowContent'; + +export default ApprovalFlowContent; +export {isApprovalFlowAction}; diff --git a/tests/ui/PureReportActionItemTest.tsx b/tests/ui/PureReportActionItemTest.tsx index 0d488e056e51..e2be1714fd15 100644 --- a/tests/ui/PureReportActionItemTest.tsx +++ b/tests/ui/PureReportActionItemTest.tsx @@ -482,6 +482,130 @@ describe('PureReportActionItem', () => { expect(screen.getByText(translateLocal('iou.submitted'))).toBeOnTheScreen(); expect(screen.queryByText(translateLocal('iou.queuedToSubmitViaDEW'))).not.toBeOnTheScreen(); }); + + it('should display DEW queued message for pending APPROVED action when policy has DEW enabled', async () => { + const action = createReportAction(CONST.REPORT.ACTIONS.TYPE.APPROVED, {automaticAction: false}); + action.pendingAction = CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD; + + const dewPolicy = { + id: 'testPolicy', + name: 'Test DEW Policy', + type: CONST.POLICY.TYPE.TEAM, + role: CONST.POLICY.ROLE.ADMIN, + owner: 'owner@test.com', + outputCurrency: CONST.CURRENCY.USD, + isPolicyExpenseChatEnabled: true, + approvalMode: CONST.POLICY.APPROVAL_MODE.DYNAMICEXTERNAL, + } as const; + + const reportMetadata = { + pendingExpenseAction: CONST.EXPENSE_PENDING_ACTION.APPROVE, + }; + + await act(async () => { + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}testPolicy`, dewPolicy); + }); + await waitForBatchedUpdatesWithAct(); + + render( + + + + + + + + + , + ); + await waitForBatchedUpdatesWithAct(); + + expect(screen.getByText(actorEmail)).toBeOnTheScreen(); + expect(screen.getByText(translateLocal('iou.queuedToApproveViaDEW'))).toBeOnTheScreen(); + }); + + it('should display submitted without memo for SUBMITTED action on DEW policy that is not pending', async () => { + const action = createReportAction(CONST.REPORT.ACTIONS.TYPE.SUBMITTED, {harvesting: false, message: 'my memo'}); + + const dewPolicy = { + id: 'testPolicy', + name: 'Test DEW Policy', + type: CONST.POLICY.TYPE.TEAM, + role: CONST.POLICY.ROLE.ADMIN, + owner: 'owner@test.com', + outputCurrency: CONST.CURRENCY.USD, + isPolicyExpenseChatEnabled: true, + approvalMode: CONST.POLICY.APPROVAL_MODE.DYNAMICEXTERNAL, + } as const; + + await act(async () => { + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}testPolicy`, dewPolicy); + }); + await waitForBatchedUpdatesWithAct(); + + render( + + + + + + + + + , + ); + await waitForBatchedUpdatesWithAct(); + + expect(screen.getByText(actorEmail)).toBeOnTheScreen(); + // DEW policy should show submitted without the memo (memo is shown in the Concierge action) + expect(screen.getByText(translateLocal('iou.submitted'))).toBeOnTheScreen(); + expect(screen.queryByText('my memo')).not.toBeOnTheScreen(); + }); + + it('CLOSED action with amount (MARK_AS_CLOSED) renders submitted message', async () => { + const action = createReportAction(CONST.REPORT.ACTIONS.TYPE.CLOSED, {amount: 5000}); + renderItemWithAction(action); + await waitForBatchedUpdatesWithAct(); + + expect(screen.getByText(translateLocal('iou.submitted'))).toBeOnTheScreen(); + }); }); describe('Followup list buttons', () => {