diff --git a/src/libs/API/parameters/AddTrackedExpenseToPolicyParams.ts b/src/libs/API/parameters/AddTrackedExpenseToPolicyParams.ts new file mode 100644 index 0000000000000..3c2441b205e46 --- /dev/null +++ b/src/libs/API/parameters/AddTrackedExpenseToPolicyParams.ts @@ -0,0 +1,28 @@ +import type {Receipt} from '@src/types/onyx/Transaction'; + +type AddTrackedExpenseToPolicyParams = { + amount: number; + currency: string; + created: string; + comment?: string; + merchant?: string; + category: string | undefined; + tag: string | undefined; + taxCode: string; + taxAmount: number; + reimbursable: boolean; + billable: boolean | undefined; + receipt: Receipt | undefined; + waypoints?: string; + customUnitRateID?: string; + policyID: string; + transactionID: string; + actionableWhisperReportActionID: string; + moneyRequestReportID: string; + reportPreviewReportActionID: string; + modifiedExpenseReportActionID: string; + moneyRequestCreatedReportActionID: string | undefined; + moneyRequestPreviewReportActionID: string; +}; + +export default AddTrackedExpenseToPolicyParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index ef4e5fee72d86..f381191e0ee2c 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -232,6 +232,7 @@ export type {default as RenamePolicyTaxParams} from './RenamePolicyTaxParams'; export type {default as UpdatePolicyTaxCodeParams} from './UpdatePolicyTaxCodeParams'; export type {default as CompleteGuidedSetupParams} from './CompleteGuidedSetupParams'; export type {default as DismissTrackExpenseActionableWhisperParams} from './DismissTrackExpenseActionableWhisperParams'; +export type {default as AddTrackedExpenseToPolicyParams} from './AddTrackedExpenseToPolicyParams'; export type {default as ConvertTrackedExpenseToRequestParams} from './ConvertTrackedExpenseToRequestParams'; export type {default as ShareTrackedExpenseParams} from './ShareTrackedExpenseParams'; export type {default as CategorizeTrackedExpenseParams} from './CategorizeTrackedExpenseParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index dda49917f9c5d..fed88bb50b718 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -297,6 +297,7 @@ const WRITE_COMMANDS = { SET_POLICY_DISTANCE_RATES_ENABLED: 'SetPolicyDistanceRatesEnabled', DELETE_POLICY_DISTANCE_RATES: 'DeletePolicyDistanceRates', DISMISS_TRACK_EXPENSE_ACTIONABLE_WHISPER: 'DismissActionableWhisper', + ADD_TRACKED_EXPENSE_TO_POLICY: 'AddTrackedExpenseToPolicy', CONVERT_TRACKED_EXPENSE_TO_REQUEST: 'ConvertTrackedExpenseToRequest', CATEGORIZE_TRACKED_EXPENSE: 'CategorizeTrackedExpense', SHARE_TRACKED_EXPENSE: 'ShareTrackedExpense', @@ -756,6 +757,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.DISMISS_TRACK_EXPENSE_ACTIONABLE_WHISPER]: Parameters.DismissTrackExpenseActionableWhisperParams; [WRITE_COMMANDS.UPDATE_BILLING_CARD_CURRENCY]: Parameters.UpdateBillingCurrencyParams; [WRITE_COMMANDS.CONVERT_TRACKED_EXPENSE_TO_REQUEST]: Parameters.ConvertTrackedExpenseToRequestParams; + [WRITE_COMMANDS.ADD_TRACKED_EXPENSE_TO_POLICY]: Parameters.AddTrackedExpenseToPolicyParams; [WRITE_COMMANDS.CATEGORIZE_TRACKED_EXPENSE]: Parameters.CategorizeTrackedExpenseParams; [WRITE_COMMANDS.SHARE_TRACKED_EXPENSE]: Parameters.ShareTrackedExpenseParams; [WRITE_COMMANDS.LEAVE_POLICY]: Parameters.LeavePolicyParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0cd321851b243..fb485da80ea9d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -124,7 +124,7 @@ import { isOptimisticPersonalDetail, isPayAtEndExpenseReport as isPayAtEndExpenseReportReportUtils, isPayer as isPayerReportUtils, - isPolicyExpenseChat as isPolicyExpenseChatReportUtils, + isPolicyExpenseChat as isPolicyExpenseChatReportUtil, isReportApproved, isSelfDM, isSettled, @@ -319,6 +319,8 @@ type RequestMoneyTransactionParams = { actionableWhisperReportActionID?: string; linkedTrackedExpenseReportAction?: OnyxTypes.ReportAction; linkedTrackedExpenseReportID?: string; + waypoints?: WaypointCollection; + customUnitRateID?: string; }; type PerDiemExpenseTransactionParams = { @@ -4125,6 +4127,38 @@ const getConvertTrackedExpenseInformation = ( return {optimisticData, successData, failureData, modifiedExpenseReportActionID: modifiedExpenseReportAction.reportActionID}; }; +type ConvertTrackedWorkspaceParams = { + category: string | undefined; + tag: string | undefined; + taxCode: string; + taxAmount: number; + billable: boolean | undefined; + policyID: string; + receipt: Receipt | undefined; + waypoints?: string; + customUnitRateID?: string; +}; + +type AddTrackedExpenseToPolicyParam = { + amount: number; + currency: string; + comment: string; + created: string; + merchant: string; + transactionID: string; + reimbursable: boolean; + actionableWhisperReportActionID: string; + moneyRequestReportID: string; + reportPreviewReportActionID: string; + modifiedExpenseReportActionID: string; + moneyRequestCreatedReportActionID: string | undefined; + moneyRequestPreviewReportActionID: string; +} & ConvertTrackedWorkspaceParams; + +function addTrackedExpenseToPolicy(parameters: AddTrackedExpenseToPolicyParam, onyxData: OnyxData) { + API.write(WRITE_COMMANDS.ADD_TRACKED_EXPENSE_TO_POLICY, parameters, onyxData); +} + function convertTrackedExpenseToRequest( payerAccountID: number, payerEmail: string, @@ -4146,6 +4180,7 @@ function convertTrackedExpenseToRequest( merchant: string, created: string, attendees?: Attendee[], + workspaceParams?: ConvertTrackedWorkspaceParams, ) { const {optimisticData, successData, failureData} = onyxData; @@ -4168,6 +4203,28 @@ function convertTrackedExpenseToRequest( successData?.push(...moveTransactionSuccessData); failureData?.push(...moveTransactionFailureData); + if (workspaceParams) { + const params = { + amount, + currency, + comment, + created, + merchant, + reimbursable: true, + transactionID, + actionableWhisperReportActionID, + moneyRequestReportID, + moneyRequestCreatedReportActionID, + moneyRequestPreviewReportActionID, + modifiedExpenseReportActionID, + reportPreviewReportActionID, + ...workspaceParams, + }; + + addTrackedExpenseToPolicy(params, {optimisticData, successData, failureData}); + return; + } + const parameters = { attendees, amount, @@ -4313,13 +4370,22 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { actionableWhisperReportActionID, linkedTrackedExpenseReportAction, linkedTrackedExpenseReportID, + waypoints, + customUnitRateID, } = transactionParams; + const sanitizedWaypoints = waypoints ? JSON.stringify(sanitizeRecentWaypoints(waypoints)) : undefined; + // If the report is iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = isMoneyRequestReportReportUtils(report); const currentChatReport = isMoneyRequestReport ? getReportOrDraftReport(report?.chatReportID) : report; const moneyRequestReportID = isMoneyRequestReport ? report?.reportID : ''; const isMovingTransactionFromTrackExpense = isMovingTransactionFromTrackExpenseIOUUtils(action); + const existingTransactionID = + isMovingTransactionFromTrackExpense && linkedTrackedExpenseReportAction && isMoneyRequestAction(linkedTrackedExpenseReportAction) + ? getOriginalMessage(linkedTrackedExpenseReportAction)?.IOUTransactionID + : undefined; + const existingTransaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${existingTransactionID}`]; const { payerAccountID, @@ -4340,10 +4406,8 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { policyParams, transactionParams, moneyRequestReportID, - existingTransactionID: - isMovingTransactionFromTrackExpense && linkedTrackedExpenseReportAction && isMoneyRequestAction(linkedTrackedExpenseReportAction) - ? getOriginalMessage(linkedTrackedExpenseReportAction)?.IOUTransactionID - : undefined, + existingTransactionID, + existingTransaction: isDistanceRequestTransactionUtils(existingTransaction) ? existingTransaction : undefined, }); const activeReportID = isMoneyRequestReport ? report?.reportID : chatReport.reportID; @@ -4352,7 +4416,20 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { if (!linkedTrackedExpenseReportAction || !actionableWhisperReportActionID || !linkedTrackedExpenseReportID) { return; } - + const workspaceParams = + isPolicyExpenseChatReportUtil(chatReport) && chatReport.policyID + ? { + receipt: receipt instanceof Blob ? receipt : undefined, + category, + tag, + taxCode, + taxAmount, + billable, + policyID: chatReport.policyID, + waypoints: sanitizedWaypoints, + customUnitRateID, + } + : undefined; convertTrackedExpenseToRequest( payerAccountID, payerEmail, @@ -4374,6 +4451,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { merchant, created, attendees, + workspaceParams, ); break; } @@ -5038,7 +5116,7 @@ function createSplitsAndOnyxData( const hasMultipleParticipants = participants.length > 1; participants.forEach((participant) => { // In a case when a participant is a workspace, even when a current user is not an owner of the workspace - const isPolicyExpenseChat = isPolicyExpenseChatReportUtils(participant); + const isPolicyExpenseChat = isPolicyExpenseChatReportUtil(participant); const splitAmount = splitShares?.[participant.accountID ?? CONST.DEFAULT_NUMBER_ID]?.amount ?? calculateIOUAmount(participants.length, amount, currency, false); const splitTaxAmount = calculateIOUAmount(participants.length, taxAmount, currency, false); @@ -5664,7 +5742,7 @@ function startSplitBill({ }); participants.forEach((participant) => { - const isPolicyExpenseChat = isPolicyExpenseChatReportUtils(participant); + const isPolicyExpenseChat = isPolicyExpenseChatReportUtil(participant); if (!isPolicyExpenseChat) { return; } @@ -7273,7 +7351,7 @@ function getReportFromHoldRequestsOnyxData( const newParentReportActionID = rand64(); const coefficient = isExpenseReport(iouReport) ? -1 : 1; - const isPolicyExpenseChat = isPolicyExpenseChatReportUtils(chatReport); + const isPolicyExpenseChat = isPolicyExpenseChatReportUtil(chatReport); const holdAmount = ((iouReport?.total ?? 0) - (iouReport?.unheldTotal ?? 0)) * coefficient; const holdNonReimbursableAmount = ((iouReport?.nonReimbursableTotal ?? 0) - (iouReport?.unheldNonReimbursableTotal ?? 0)) * coefficient; const optimisticExpenseReport = isPolicyExpenseChat @@ -7852,7 +7930,7 @@ function canIOUBePaid( invoiceReceiverPolicy?: SearchPolicy, shouldCheckApprovedState = true, ) { - const isPolicyExpenseChat = isPolicyExpenseChatReportUtils(chatReport); + const isPolicyExpenseChat = isPolicyExpenseChatReportUtil(chatReport); const reportNameValuePairs = chatReportRNVP ?? getReportNameValuePairs(chatReport?.reportID); const isChatReportArchived = isArchivedReport(reportNameValuePairs); const iouSettled = isSettled(iouReport); @@ -8769,8 +8847,8 @@ function setMoneyRequestParticipantsFromReport(transactionID: string, report: On const shouldAddAsReport = !isEmptyObject(chatReport) && isSelfDM(chatReport); let participants: Participant[] = []; - if (isPolicyExpenseChatReportUtils(chatReport) || shouldAddAsReport) { - participants = [{accountID: 0, reportID: chatReport?.reportID, isPolicyExpenseChat: isPolicyExpenseChatReportUtils(chatReport), selected: true}]; + if (isPolicyExpenseChatReportUtil(chatReport) || shouldAddAsReport) { + participants = [{accountID: 0, reportID: chatReport?.reportID, isPolicyExpenseChat: isPolicyExpenseChatReportUtil(chatReport), selected: true}]; } else if (isInvoiceRoom(chatReport)) { participants = [ {reportID: chatReport?.reportID, selected: true}, diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 52a086315e5c9..6303155eebbe6 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -22,6 +22,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import {isMovingTransactionFromTrackExpense} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; +import type {Section} from '@libs/OptionsListUtils'; import { filterAndOrderOptions, formatSectionsFromSearchTerm, @@ -33,7 +34,6 @@ import { isCurrentUser, orderOptions, } from '@libs/OptionsListUtils'; -import type {Section} from '@libs/OptionsListUtils'; import {isPaidGroupPolicy as isPaidGroupPolicyUtil} from '@libs/PolicyUtils'; import type {OptionData} from '@libs/ReportUtils'; import {isInvoiceRoom} from '@libs/ReportUtils'; @@ -115,8 +115,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF // If we are using this component in the "Submit expense" or the combined submit/track flow then we pass the includeOwnedWorkspaceChats argument so that the current user // sees the option to submit an expense from their admin on their own Workspace Chat. - includeOwnedWorkspaceChats: - (iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.SPLIT) && action !== CONST.IOU.ACTION.SUBMIT, + includeOwnedWorkspaceChats: iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.SPLIT, includeP2P: !isCategorizeOrShareAction, includeInvoiceRooms: iouType === CONST.IOU.TYPE.INVOICE, diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index a54c882259c83..ead1d2a1fc3aa 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -277,10 +277,24 @@ function IOURequestStepConfirmation({ actionableWhisperReportActionID: transaction.actionableWhisperReportActionID, linkedTrackedExpenseReportAction: transaction.linkedTrackedExpenseReportAction, linkedTrackedExpenseReportID: transaction.linkedTrackedExpenseReportID, + waypoints: Object.keys(transaction.comment?.waypoints ?? {}).length ? getValidWaypoints(transaction.comment?.waypoints, true) : undefined, + customUnitRateID, }, }); }, - [report, transaction, transactionTaxCode, transactionTaxAmount, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, policy, policyTags, policyCategories, action], + [ + report, + transaction, + transactionTaxCode, + transactionTaxAmount, + currentUserPersonalDetails.login, + currentUserPersonalDetails.accountID, + policy, + policyTags, + policyCategories, + action, + customUnitRateID, + ], ); const submitPerDiemExpense = useCallback(