diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 63d0b634a6f35..b0b22b0da0802 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -274,6 +274,8 @@ function MoneyRequestConfirmationList({ selector: mileageRateSelector, canBeMissing: true, }); + const {policyForMovingExpenses} = usePolicyForMovingExpenses(); + const isMovingTransactionFromTrackExpense = isMovingTransactionFromTrackExpenseUtil(action); const [defaultMileageRateReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { selector: mileageRateSelector, canBeMissing: true, @@ -301,7 +303,6 @@ function MoneyRequestConfirmationList({ isTestDriveReceipt || isManagerMcTestReceipt, ); - const {policyForMovingExpenses} = usePolicyForMovingExpenses(); const isTrackExpense = iouType === CONST.IOU.TYPE.TRACK; const policy = isTrackExpense ? policyForMovingExpenses : (policyReal ?? policyDraft); const policyCategories = policyCategoriesReal ?? policyCategoriesDraft; @@ -319,7 +320,6 @@ function MoneyRequestConfirmationList({ const isTypeInvoice = iouType === CONST.IOU.TYPE.INVOICE; const isScanRequest = useMemo(() => isScanRequestUtil(transaction), [transaction]); const isCreateExpenseFlow = !!transaction?.isFromGlobalCreate && !isPerDiemRequest; - const isMovingTransactionFromTrackExpense = isMovingTransactionFromTrackExpenseUtil(action); const transactionID = transaction?.transactionID; const customUnitRateID = getRateID(transaction); @@ -363,16 +363,18 @@ function MoneyRequestConfirmationList({ const policyTagLists = useMemo(() => getTagLists(policyTags), [policyTags]); - const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest, isPerDiemRequest, isTimeRequest); + const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat || isTrackExpense, policy, isDistanceRequest, isPerDiemRequest, isTimeRequest); // Update the tax code when the default changes (for example, because the transaction currency changed) - const defaultTaxCode = getDefaultTaxCode(policy, transaction) ?? ''; + const defaultTaxCode = getDefaultTaxCode(policy, transaction) ?? (isMovingTransactionFromTrackExpense ? (getDefaultTaxCode(policyForMovingExpenses, transaction) ?? '') : ''); + useEffect(() => { - if (!transactionID || isReadOnly || !shouldShowTax) { + if (!transactionID || isReadOnly || !shouldShowTax || isMovingTransactionFromTrackExpense) { return; } setMoneyRequestTaxRate(transactionID, defaultTaxCode); - }, [defaultTaxCode, transactionID, isReadOnly, shouldShowTax]); + // trigger this useEffect also when policyID changes - the defaultTaxCode may stay the same + }, [defaultTaxCode, isMovingTransactionFromTrackExpense, isReadOnly, transactionID, policyID, shouldShowTax]); const distance = getDistanceInMeters(transaction, unit); const prevDistance = usePrevious(distance); @@ -532,7 +534,10 @@ function MoneyRequestConfirmationList({ // Calculate and set tax amount in transaction draft const taxableAmount = isDistanceRequest ? DistanceRequestUtils.getTaxableAmount(policy, customUnitRateID, distance) : (transaction?.amount ?? 0); - const taxPercentage = getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? ''; + // First we'll try to get the tax value from the chosen policy and if not found, we'll try to get it from the policy for moving expenses (only if the transaction is moving from track expense) + const taxPercentage = + getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? + (isMovingTransactionFromTrackExpense ? getTaxValue(policyForMovingExpenses, transaction, transaction?.taxCode ?? defaultTaxCode) : ''); const taxAmount = calculateTaxAmount(taxPercentage, taxableAmount, transaction?.currency ?? CONST.CURRENCY.USD); const taxAmountInSmallestCurrencyUnits = convertToBackendAmount(Number.parseFloat(taxAmount.toString())); useEffect(() => { @@ -869,7 +874,7 @@ function MoneyRequestConfirmationList({ if (!transactionID || iouCategory || !shouldShowCategories || enabledCategories.length !== 1 || !isCategoryRequired) { return; } - setMoneyRequestCategory(transactionID, enabledCategories.at(0)?.name ?? '', policy); + setMoneyRequestCategory(transactionID, enabledCategories.at(0)?.name ?? '', policy, isMovingTransactionFromTrackExpense); // Keep 'transaction' out to ensure that we auto select the option only once // eslint-disable-next-line react-hooks/exhaustive-deps }, [shouldShowCategories, policyCategories, isCategoryRequired, policy?.id]); @@ -962,6 +967,11 @@ function MoneyRequestConfirmationList({ return; } + if (shouldShowTax && !!transaction.taxCode && !Object.keys(policy?.taxRates?.taxes ?? {}).some((key) => key === transaction.taxCode)) { + setFormError('violations.taxOutOfPolicy'); + return; + } + if (isPerDiemRequest && (transaction.comment?.customUnit?.subRates ?? []).length === 0) { setFormError('iou.error.invalidSubrateLength'); return; @@ -1029,6 +1039,7 @@ function MoneyRequestConfirmationList({ isMerchantRequired, isMerchantEmpty, shouldDisplayFieldError, + shouldShowTax, transaction, policyTags, isPerDiemRequest, diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index b837f72eba234..eb14d29548430 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -17,7 +17,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {getDecodedCategoryName} from '@libs/CategoryUtils'; import {convertToDisplayString} from '@libs/CurrencyUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; -import {shouldShowReceiptEmptyState} from '@libs/IOUUtils'; +import {isMovingTransactionFromTrackExpense, shouldShowReceiptEmptyState} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getTimeDifferenceIntervals, getTimeForDisplay} from '@libs/PerDiemRequestUtils'; import {canSendInvoice, getPerDiemCustomUnit} from '@libs/PolicyUtils'; @@ -293,7 +293,7 @@ function MoneyRequestConfirmationListFooter({ const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, { canBeMissing: true, }); - const {policyForMovingExpensesID, shouldSelectPolicy} = usePolicyForMovingExpenses(); + const {policyForMovingExpensesID, policyForMovingExpenses, shouldSelectPolicy} = usePolicyForMovingExpenses(); const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: emailSelector, canBeMissing: true}); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); @@ -379,11 +379,13 @@ function MoneyRequestConfirmationListFooter({ const shouldReportBeEditableFromFAB = isUnreported ? allOutstandingReports.length >= 1 : allOutstandingReports.length > 1; + const isMovingCurrentTransactionFromTrackExpense = isMovingTransactionFromTrackExpense(action); + // When creating an expense in an individual report, the report field becomes read-only // since the destination is already determined and there's no need to show a selectable list. const shouldReportBeEditable = (isFromGlobalCreate && !isPerDiemRequest ? shouldReportBeEditableFromFAB : availableOutstandingReports.length > 1) && !isMoneyRequestReport(reportID, allReports); - const taxRates = policy?.taxRates ?? null; + const taxRates = policy?.taxRates ?? (isMovingCurrentTransactionFromTrackExpense ? policyForMovingExpenses?.taxRates : null); // In Send Money and Split Bill with Scan flow, we don't allow the Merchant or Date to be edited. For distance requests, don't show the merchant as there's already another "Distance" menu item const shouldShowDate = shouldShowSmartScanFields || isDistanceRequest; // Determines whether the tax fields can be modified. @@ -398,11 +400,19 @@ function MoneyRequestConfirmationListFooter({ const taxAmount = getTaxAmount(transaction, false); const formattedTaxAmount = convertToDisplayString(taxAmount, iouCurrencyCode); // Get the tax rate title based on the policy and transaction - const taxRateTitle = getTaxName(policy, transaction); + let taxRateTitle; + if (getTaxName(policy, transaction)) { + taxRateTitle = getTaxName(policy, transaction); + } else if (isMovingCurrentTransactionFromTrackExpense) { + taxRateTitle = getTaxName(policyForMovingExpenses, transaction); + } else { + taxRateTitle = ''; + } // Determine if the merchant error should be displayed const shouldDisplayMerchantError = isMerchantRequired && (shouldDisplayFieldError || formError === 'iou.error.invalidMerchant') && isMerchantEmpty; const shouldDisplayDistanceRateError = formError === 'iou.error.invalidRate'; const shouldDisplayTagError = formError === 'violations.tagOutOfPolicy'; + const shouldDisplayTaxRateError = formError === 'violations.taxOutOfPolicy'; const shouldDisplayCategoryError = formError === 'violations.categoryOutOfPolicy'; const shouldDisplayAttendeesError = formError === 'violations.missingAttendees'; @@ -769,6 +779,8 @@ function MoneyRequestConfirmationListFooter({ }} disabled={didConfirm} interactive={canModifyTaxFields} + brickRoadIndicator={shouldDisplayTaxRateError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + errorText={shouldDisplayTaxRateError ? translate(formError) : ''} /> ), shouldShow: shouldShowTax, diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 2473870179791..a7c47440276db 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -309,7 +309,7 @@ function MoneyRequestView({ : convertToDisplayString(Math.abs(transactionTaxAmount ?? 0), transactionCurrency); const taxRatesDescription = taxRates?.name; - const taxRateTitle = updatedTransaction ? getTaxName(policy, updatedTransaction) : getTaxName(policy, transaction); + const taxRateTitle = updatedTransaction ? getTaxName(policy, updatedTransaction, isExpenseUnreported) : getTaxName(policy, transaction, isExpenseUnreported); const actualTransactionDate = isFromMergeTransaction && updatedTransaction ? getFormattedCreated(updatedTransaction, preferredLocale) : transactionDate; const fallbackTaxRateTitle = transaction?.taxValue; @@ -395,7 +395,7 @@ function MoneyRequestView({ canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.REIMBURSABLE, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy); const shouldShowAttendees = shouldShowAttendeesTransactionUtils(iouType, policy); - const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest, isPerDiemRequest, isTimeRequest); + const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat || isExpenseUnreported, policy, isDistanceRequest, isPerDiemRequest, isTimeRequest); const tripID = getTripIDFromTransactionParentReportID(parentReport?.parentReportID); const shouldShowViewTripDetails = hasReservationList(transaction) && !!tripID; diff --git a/src/hooks/usePolicyForTransaction.ts b/src/hooks/usePolicyForTransaction.ts new file mode 100644 index 0000000000000..31f33c9b5c811 --- /dev/null +++ b/src/hooks/usePolicyForTransaction.ts @@ -0,0 +1,39 @@ +import type {OnyxEntry} from 'react-native-onyx'; +import {isExpenseUnreported} from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Policy, Report, Transaction} from '@src/types/onyx'; +import useOnyx from './useOnyx'; +import usePolicyForMovingExpenses from './usePolicyForMovingExpenses'; + +type UsePolicyForTransactionParams = { + /** The transaction to determine the policy for */ + transaction: OnyxEntry; + + /** The report associated with the transaction */ + report: OnyxEntry; + + /** The current action being performed */ + action: string; + + /** The type of IOU (split, track, submit, etc.) */ + iouType: string; +}; + +type UsePolicyForTransactionResult = { + /** The policy to use for the transaction */ + policy: OnyxEntry; +}; + +function usePolicyForTransaction({transaction, report, action, iouType}: UsePolicyForTransactionParams): UsePolicyForTransactionResult { + const {policyForMovingExpenses} = usePolicyForMovingExpenses(); + const isUnreportedExpense = isExpenseUnreported(transaction); + const isCreatingTrackExpense = action === CONST.IOU.ACTION.CREATE && iouType === CONST.IOU.TYPE.TRACK; + + const [reportPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); + const policy = isUnreportedExpense || isCreatingTrackExpense ? policyForMovingExpenses : reportPolicy; + + return {policy}; +} + +export default usePolicyForTransaction; diff --git a/src/libs/API/parameters/TrackExpenseParams.ts b/src/libs/API/parameters/TrackExpenseParams.ts index 7cf42fd2e36eb..42f8b878c6783 100644 --- a/src/libs/API/parameters/TrackExpenseParams.ts +++ b/src/libs/API/parameters/TrackExpenseParams.ts @@ -19,6 +19,7 @@ type TrackExpenseParams = { reportPreviewReportActionID?: string; optimisticReportID?: string; optimisticReportActionID?: string; + policyID?: string; receipt?: Receipt; receiptState?: ValueOf; category?: string; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 63ae6dcc22ff5..b2bee250eb7c3 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -666,13 +666,19 @@ function isCollectPolicy(policy: OnyxEntry): boolean { return policy?.type === CONST.POLICY.TYPE.TEAM; } -function isTaxTrackingEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry, isDistanceRequest: boolean, isPerDiemRequest = false, isTimeRequest = false): boolean { +function isTaxTrackingEnabled( + isPolicyExpenseChatOrUnreportedExpense: boolean, + policy: OnyxEntry, + isDistanceRequest: boolean, + isPerDiemRequest = false, + isTimeRequest = false, +): boolean { if (isPerDiemRequest || isTimeRequest) { return false; } const distanceUnit = getDistanceRateCustomUnit(policy); const customUnitID = distanceUnit?.customUnitID ?? CONST.DEFAULT_NUMBER_ID; - const isPolicyTaxTrackingEnabled = isPolicyExpenseChat && policy?.tax?.trackingEnabled; + const isPolicyTaxTrackingEnabled = isPolicyExpenseChatOrUnreportedExpense && policy?.tax?.trackingEnabled; const isTaxEnabledForDistance = isPolicyTaxTrackingEnabled && !!customUnitID && policy?.customUnits?.[customUnitID]?.attributes?.taxEnabled; return !!(isDistanceRequest ? isTaxEnabledForDistance : isPolicyTaxTrackingEnabled); diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index d28587dc360d7..8fb5dfa83b281 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -743,6 +743,10 @@ function getUpdatedTransaction({ updatedTransaction.taxCode = transactionChanges.taxCode; } + if (Object.hasOwn(transactionChanges, 'taxValue') && typeof transactionChanges.taxCode === 'string') { + updatedTransaction.taxValue = transactionChanges.taxValue; + } + if (Object.hasOwn(transactionChanges, 'reimbursable') && typeof transactionChanges.reimbursable === 'boolean') { updatedTransaction.reimbursable = transactionChanges.reimbursable; } @@ -2070,9 +2074,18 @@ function getWorkspaceTaxesSettingsName(policy: OnyxEntry, taxCode: strin /** * Gets the name corresponding to the taxCode that is displayed to the user */ -function getTaxName(policy: OnyxEntry, transaction: OnyxEntry) { +function getTaxName(policy: OnyxEntry, transaction: OnyxEntry, shouldFallbackToValue = false) { const defaultTaxCode = getDefaultTaxCode(policy, transaction); - return Object.values(transformedTaxRates(policy, transaction)).find((taxRate) => taxRate.code === (transaction?.taxCode ?? defaultTaxCode))?.modifiedName; + + // transaction?.taxCode may be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const taxRate = Object.values(transformedTaxRates(policy, transaction)).find((rate) => rate.code === (transaction?.taxCode || defaultTaxCode)); + + if (shouldFallbackToValue && transaction?.taxValue !== undefined && taxRate?.value !== transaction?.taxValue) { + return transaction?.taxValue; + } + + return taxRate?.modifiedName; } type FieldsToCompare = Record>; diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index 0d56bf558e081..e10d8dbfa938f 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -1314,9 +1314,13 @@ function setMoneyRequestTimeCount(transactionID: string, count: number, isDraft: * @param transactionID - The transaction ID * @param category - The category name * @param policy - The policy object, or undefined for P2P transactions where tax info should be cleared + * @param isMovingFromTrackExpense - If the expense is moved from Track Expense */ -function setMoneyRequestCategory(transactionID: string, category: string, policy: OnyxEntry) { +function setMoneyRequestCategory(transactionID: string, category: string, policy: OnyxEntry, isMovingFromTrackExpense?: boolean) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {category}); + if (isMovingFromTrackExpense) { + return; + } if (!policy) { setMoneyRequestTaxRate(transactionID, ''); setMoneyRequestTaxAmount(transactionID, null); @@ -5203,6 +5207,7 @@ type UpdateMoneyRequestTaxRateParams = { parentReport: OnyxEntry; taxCode: string; taxAmount: number; + taxValue: string; policy: OnyxEntry; policyTagList: OnyxEntry; policyCategories: OnyxEntry; @@ -5219,6 +5224,7 @@ function updateMoneyRequestTaxRate({ parentReport, taxCode, taxAmount, + taxValue, policy, policyTagList, policyCategories, @@ -5230,6 +5236,7 @@ function updateMoneyRequestTaxRate({ const transactionChanges = { taxCode, taxAmount, + taxValue, }; const {params, onyxData} = getUpdateMoneyRequestParams({ transactionID, @@ -6265,7 +6272,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation): {iouRep category, tag, taxCode, - taxAmount, + taxAmount: Math.abs(taxAmount), billable, policyID: chatReport.policyID, waypoints: sanitizedWaypoints, @@ -6796,6 +6803,7 @@ function trackExpense(params: CreateTrackExpenseParams) { reportPreviewReportActionID: reportPreviewAction?.reportActionID, optimisticReportID, optimisticReportActionID, + policyID: policy?.id, receipt: isFileUploadable(trackedReceipt) ? trackedReceipt : undefined, receiptState: trackedReceipt?.state, reimbursable, @@ -8539,6 +8547,7 @@ type UpdateMoneyRequestAmountAndCurrencyParams = { policyTagList?: OnyxEntry; policyCategories?: OnyxEntry; taxCode: string; + taxValue: string; allowNegative?: boolean; transactions: OnyxCollection; transactionViolations: OnyxCollection; @@ -8561,6 +8570,7 @@ function updateMoneyRequestAmountAndCurrency({ policyTagList, policyCategories, taxCode, + taxValue, allowNegative = false, transactions, transactionViolations, @@ -8575,6 +8585,7 @@ function updateMoneyRequestAmountAndCurrency({ currency, taxCode, taxAmount, + taxValue, }; let data: UpdateMoneyRequestData; @@ -11678,6 +11689,7 @@ function completePaymentOnboarding( companySize: introSelected?.companySize as OnboardingCompanySize, }); } + function payMoneyRequest( paymentType: PaymentMethodType, chatReport: OnyxTypes.Report, diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index e6358feae9cfb..2049e518ef147 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -11,13 +11,14 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; import usePersonalPolicy from '@hooks/usePersonalPolicy'; +import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import usePrivateIsArchivedMap from '@hooks/usePrivateIsArchivedMap'; import useReportIsArchived from '@hooks/useReportIsArchived'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; import {setTransactionReport} from '@libs/actions/Transaction'; import {convertToBackendAmount} from '@libs/CurrencyUtils'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; -import {navigateToConfirmationPage, navigateToParticipantPage} from '@libs/IOUUtils'; +import {isMovingTransactionFromTrackExpense, navigateToConfirmationPage, navigateToParticipantPage} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; import {isPaidGroupPolicy} from '@libs/PolicyUtils'; @@ -32,6 +33,8 @@ import { setDraftSplitTransaction, setMoneyRequestAmount, setMoneyRequestParticipantsFromReport, + setMoneyRequestTaxAmount, + setMoneyRequestTaxRate, setSplitShares, trackExpense, updateMoneyRequestAmountAndCurrency, @@ -80,7 +83,9 @@ function IOURequestStepAmount({ const focusTimeoutRef = useRef(null); const isSaveButtonPressed = useRef(false); const iouRequestType = getRequestType(transaction); - const policyID = report?.policyID; + const isTrackExpense = iouType === CONST.IOU.TYPE.TRACK; + const {policyForMovingExpensesID} = usePolicyForMovingExpenses(); + const policyID = isTrackExpense ? policyForMovingExpensesID : report?.policyID; const isReportArchived = useReportIsArchived(report?.reportID); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: true}); @@ -183,6 +188,16 @@ function IOURequestStepAmount({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing setMoneyRequestAmount(transactionID, amountInSmallestCurrencyUnits, selectedCurrency || CONST.CURRENCY.USD, shouldKeepUserInput); + if (isMovingTransactionFromTrackExpense(action)) { + const taxCode = selectedCurrency !== policy?.outputCurrency ? policy?.taxRates?.foreignTaxDefault : policy?.taxRates?.defaultExternalID; + if (taxCode) { + setMoneyRequestTaxRate(transactionID, taxCode); + const taxPercentage = getTaxValue(policy, transaction, taxCode) ?? ''; + const taxAmount = convertToBackendAmount(calculateTaxAmount(taxPercentage, amountInSmallestCurrencyUnits, selectedCurrency || CONST.CURRENCY.USD)); + setMoneyRequestTaxAmount(transactionID, taxAmount); + } + } + if (backTo) { Navigation.goBack(backTo); return; @@ -368,6 +383,7 @@ function IOURequestStepAmount({ taxAmount, policy, taxCode, + taxValue: taxPercentage, policyCategories, currentUserAccountIDParam, currentUserEmailParam, diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index bef2af8846101..b8e6f473c867e 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -412,7 +412,7 @@ function IOURequestStepConfirmation({ if (!isDistanceRequest || !!item?.category) { continue; } - setMoneyRequestCategory(item.transactionID, defaultCategory, policy); + setMoneyRequestCategory(item.transactionID, defaultCategory, policy, isMovingTransactionFromTrackExpense); } // Prevent resetting to default when unselect category // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index ee1a7a78903c9..a83880c8a8767 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -7,6 +7,7 @@ import FormHelpMessage from '@components/FormHelpMessage'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import useThemeStyles from '@hooks/useThemeStyles'; import {setTransactionReport} from '@libs/actions/Transaction'; import {READ_COMMANDS} from '@libs/API/types'; @@ -77,6 +78,7 @@ function IOURequestStepParticipants({ const {translate} = useLocalize(); const styles = useThemeStyles(); const isFocused = useIsFocused(); + const {policyForMovingExpenses} = usePolicyForMovingExpenses(); const [skipConfirmation] = useOnyx(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${initialTransactionID}`, {canBeMissing: true}); const [optimisticTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, { selector: transactionDraftValuesSelector, @@ -336,7 +338,7 @@ function IOURequestStepParticipants({ const policyDistance = Object.values(policy?.customUnits ?? {}).find((customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); const defaultCategory = isDistanceRequest(transaction) && policyDistance?.defaultCategory ? policyDistance?.defaultCategory : ''; const category = isMovingTransactionFromTrackExpense ? (transaction?.category ?? '') : defaultCategory; - setMoneyRequestCategory(transaction.transactionID, category, undefined); + setMoneyRequestCategory(transaction.transactionID, category, isMovingTransactionFromTrackExpense ? policyForMovingExpenses : undefined, isMovingTransactionFromTrackExpense); if (shouldUpdateTransactionReportID) { setTransactionReport(transaction.transactionID, {reportID: transactionReportID}, true); } @@ -405,6 +407,7 @@ function IOURequestStepParticipants({ transactions, isMovingTransactionFromTrackExpense, allPolicies, + policyForMovingExpenses, introSelected, backTo, ], diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index 7399cc80da079..251b0861bcee0 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -6,6 +6,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePolicyForTransaction from '@hooks/usePolicyForTransaction'; import useRestartOnReceiptFailure from '@hooks/useRestartOnReceiptFailure'; import {setDraftSplitTransaction, setMoneyRequestCurrency, setMoneyRequestParticipantsFromReport, setMoneyRequestTaxAmount, updateMoneyRequestTaxAmount} from '@libs/actions/IOU'; import {convertToBackendAmount} from '@libs/CurrencyUtils'; @@ -52,9 +53,10 @@ function IOURequestStepTaxAmountPage({ transaction, report, }: IOURequestStepTaxAmountPageProps) { - const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); - const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report?.policyID}`, {canBeMissing: true}); - const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${report?.policyID}`, {canBeMissing: true}); + const {policy} = usePolicyForTransaction({transaction, report, action, iouType}); + + const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policy?.id}`, {canBeMissing: true}); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policy?.id}`, {canBeMissing: true}); const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, {canBeMissing: true}); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.parentReportID)}`, {canBeMissing: true}); const [parentReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${getNonEmptyStringOnyxID(report?.parentReportID)}`, {canBeMissing: true}); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index ea23bda93bc08..c6a6970e54a72 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -5,6 +5,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePolicyForTransaction from '@hooks/usePolicyForTransaction'; import useRestartOnReceiptFailure from '@hooks/useRestartOnReceiptFailure'; import {convertToBackendAmount} from '@libs/CurrencyUtils'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; @@ -41,8 +42,8 @@ function IOURequestStepTaxRatePage({ report, }: IOURequestStepTaxRatePageProps) { const {translate} = useLocalize(); + const {policy} = usePolicyForTransaction({transaction, report, action, iouType}); - const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policy?.id}`, {canBeMissing: true}); const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policy?.id}`, {canBeMissing: true}); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.parentReportID)}`, {canBeMissing: true}); @@ -74,6 +75,7 @@ function IOURequestStepTaxRatePage({ } const taxAmount = getTaxAmount(policy, currentTransaction, taxes.code, getAmount(currentTransaction, false, true)); + const taxValue = getTaxValue(policy, currentTransaction, taxes.code) ?? ''; if (isEditingSplitBill) { setDraftSplitTransaction(currentTransaction.transactionID, splitDraftTransaction, { @@ -91,6 +93,7 @@ function IOURequestStepTaxRatePage({ transactionThreadReport: report, parentReport, taxCode: newTaxCode, + taxValue, taxAmount: convertToBackendAmount(taxAmount ?? 0), policy, policyTagList: policyTags, diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index c4186682b97c1..420e0fa81b04c 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -5462,6 +5462,7 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, taxAmount: 0, taxCode: '', + taxValue: '', policy: { id: '123', role: CONST.POLICY.ROLE.USER, @@ -8118,6 +8119,7 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, taxAmount: 0, taxCode: '', + taxValue: '', policy: { id: '123', role: CONST.POLICY.ROLE.USER, @@ -8187,6 +8189,7 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, taxAmount: 0, taxCode: '', + taxValue: '', policy: { id: '123', role: CONST.POLICY.ROLE.USER, @@ -8285,6 +8288,7 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, taxAmount: 0, taxCode: '', + taxValue: '', policy, policyTagList: {}, policyCategories: {},