diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 084143492835..81d237c500f9 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -53,6 +53,7 @@ import { getLengthOfTag, getPerDiemCustomUnit, getPolicyByCustomUnitID, + getPolicyByCustomUnitRateID, getTagLists, hasDependentTags as hasDependentTagsPolicyUtils, isAttendeeTrackingEnabled, @@ -209,19 +210,26 @@ function MoneyRequestView({ const [policiesWithPerDiem] = useOnyx(ONYXKEYS.COLLECTION.POLICY, { selector: perDiemPoliciesSelector, }); + const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const isPerDiemRequest = isPerDiemRequestTransactionUtils(transaction); const perDiemOriginalPolicy = getPolicyByCustomUnitID(transaction, policiesWithPerDiem); + const isDistanceRequestForPolicy = isDistanceRequestTransactionUtils(transaction); + const distanceOriginalPolicy = isDistanceRequestForPolicy ? getPolicyByCustomUnitRateID(transaction, allPolicies) : undefined; let policy; let policyID; // If the expense is unreported the policy should be the user's default policy, if the expense is a per diem request and is unreported - // the policy should be the one where the per diem rates are enabled, otherwise it should be the expense's report policy - if (isExpenseUnreported && !isPerDiemRequest) { - policy = policyForMovingExpenses; - policyID = policyForMovingExpensesID; + // the policy should be the one where the per diem rates are enabled, if the expense is a distance request and is unreported + // the policy should be the one where the distance rate belongs to, otherwise it should be the expense's report policy + if (isExpenseUnreported && isDistanceRequestForPolicy && distanceOriginalPolicy) { + policy = distanceOriginalPolicy; + policyID = distanceOriginalPolicy?.id; } else if (isExpenseUnreported && isPerDiemRequest) { policy = perDiemOriginalPolicy; policyID = perDiemOriginalPolicy?.id; + } else if (isExpenseUnreported) { + policy = policyForMovingExpenses; + policyID = policyForMovingExpensesID; } else { policy = expensePolicy; policyID = parentReport?.policyID; diff --git a/src/hooks/usePolicyForTransaction.ts b/src/hooks/usePolicyForTransaction.ts index 432ba0e6a704..24914d73907b 100644 --- a/src/hooks/usePolicyForTransaction.ts +++ b/src/hooks/usePolicyForTransaction.ts @@ -1,7 +1,7 @@ import {useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; -import {getPolicyByCustomUnitID} from '@libs/PolicyUtils'; -import {isExpenseUnreported} from '@libs/TransactionUtils'; +import {getPolicyByCustomUnitID, getPolicyByCustomUnitRateID} from '@libs/PolicyUtils'; +import {isDistanceRequest, isExpenseUnreported} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Transaction} from '@src/types/onyx'; @@ -42,12 +42,26 @@ function usePolicyForTransaction({transaction, reportPolicyID, action, iouType, return getPolicyByCustomUnitID(transaction, allPolicies); }, [transaction, allPolicies]); + const distanceRatePolicy = useMemo(() => { + return getPolicyByCustomUnitRateID(transaction, allPolicies); + }, [transaction, allPolicies]); + const [reportPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${reportPolicyID}`); const isUnreportedExpense = isExpenseUnreported(transaction); const isCreatingTrackExpense = action === CONST.IOU.ACTION.CREATE && iouType === CONST.IOU.TYPE.TRACK; + const isDistanceExpense = isDistanceRequest(transaction); - const policyForSelfDMExpense = isPerDiemRequest ? customUnitPolicy : policyForMovingExpenses; + const getPolicyForSelfDMExpense = () => { + if (isPerDiemRequest) { + return customUnitPolicy; + } + if (isDistanceExpense && distanceRatePolicy) { + return distanceRatePolicy; + } + return policyForMovingExpenses; + }; + const policyForSelfDMExpense = getPolicyForSelfDMExpense(); const policy = isUnreportedExpense || isCreatingTrackExpense ? policyForSelfDMExpense : (reportPolicy ?? policyDraft); return {policy}; diff --git a/src/libs/API/parameters/UpdateMoneyRequestParams.ts b/src/libs/API/parameters/UpdateMoneyRequestParams.ts index 68dbb7a8f679..2d0eb5b55509 100644 --- a/src/libs/API/parameters/UpdateMoneyRequestParams.ts +++ b/src/libs/API/parameters/UpdateMoneyRequestParams.ts @@ -4,6 +4,7 @@ type UpdateMoneyRequestParams = Partial & { reportID?: string; transactionID?: string; reportActionID?: string; + policyID?: string; /** Used for bulk updates - JSON stringified object containing only changed fields */ updates?: string; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index ce8f511615b1..d97cefdecf5f 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -234,6 +234,22 @@ function getPolicyByCustomUnitID(transaction: OnyxEntry, policies: }); } +/** + * Finds a policy that contains the customUnitRateID from the transaction's distance rate + */ +function getPolicyByCustomUnitRateID(transaction: OnyxEntry, policies: OnyxCollection): OnyxEntry { + const customUnitRateID = transaction?.comment?.customUnit?.customUnitRateID; + + if (!customUnitRateID || !policies) { + return undefined; + } + + return Object.values(policies).find((policy) => { + const distanceUnit = getDistanceRateCustomUnit(policy); + return !!distanceUnit?.rates && customUnitRateID in distanceUnit.rates; + }); +} + /** * Retrieves custom unit rate object from the given customUnitRateID */ @@ -2208,6 +2224,7 @@ export { getDistanceRateCustomUnit, getPerDiemCustomUnit, getPolicyByCustomUnitID, + getPolicyByCustomUnitRateID, getDistanceRateCustomUnitRate, getPerDiemRateCustomUnitRate, sortWorkspacesBySelected, diff --git a/src/libs/actions/IOU/UpdateMoneyRequest.ts b/src/libs/actions/IOU/UpdateMoneyRequest.ts index 5ebc61c00b8a..de983e275eaa 100644 --- a/src/libs/actions/IOU/UpdateMoneyRequest.ts +++ b/src/libs/actions/IOU/UpdateMoneyRequest.ts @@ -964,6 +964,7 @@ function getUpdateMoneyRequestParams(params: GetUpdateMoneyRequestParamsType): U ...dataToIncludeInParams, reportID: iouReport?.reportID, transactionID, + policyID: policy?.id, }; const hasPendingWaypoints = 'waypoints' in transactionChanges; @@ -1482,6 +1483,7 @@ function getUpdateTrackExpenseParams( ...dataToIncludeInParams, reportID: chatReport?.reportID, transactionID, + policyID: policy?.id, }; const hasPendingWaypoints = 'waypoints' in transactionChanges; diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index 3ff9a2f0a605..6eba9cf668d6 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -10,6 +10,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import usePolicyForTransaction from '@hooks/usePolicyForTransaction'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -66,6 +67,7 @@ function IOURequestStepDistanceRate({ const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`); const {policy} = usePolicyForTransaction({transaction, reportPolicyID: report?.policyID, action, iouType, policyDraft}); + const {policyForMovingExpenses} = usePolicyForMovingExpenses(); const styles = useThemeStyles(); const {translate, toLocaleDigit, localeCompare} = useLocalize(); @@ -76,7 +78,8 @@ function IOURequestStepDistanceRate({ const {getCurrencySymbol, getCurrencyDecimals} = useCurrencyListActions(); const isPolicyExpenseChat = isReportInGroupPolicy(report); const isTrackExpense = iouType === CONST.IOU.TYPE.TRACK; - const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat || isTrackExpense || isExpenseUnreported(currentTransaction), policy, isDistanceRequest); + const isUnreportedExpense = isExpenseUnreported(currentTransaction); + const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat || isTrackExpense || isUnreportedExpense, policy, isDistanceRequest); const currentRateID = getRateID(currentTransaction); const transactionCurrency = getCurrency(currentTransaction); @@ -91,7 +94,11 @@ function IOURequestStepDistanceRate({ // This keeps the problematic rate shown as selected so the user understands what they need to change. const [pendingRateID, setPendingRateID] = useState(); - const rates = DistanceRequestUtils.getMileageRates(policy, false, currentRateID); + // For unreported distance expenses, use the default policy for the rate picker list + // so users see rates from their default workspace, not the original non-default workspace. + // The `policy` (from usePolicyForTransaction) still reflects the original policy for display purposes. + const policyForRates = isUnreportedExpense && isDistanceRequest && policyForMovingExpenses ? policyForMovingExpenses : policy; + const rates = DistanceRequestUtils.getMileageRates(policyForRates, false, currentRateID); const isMovingTransactionFromTrackExpense = isMovingTransactionFromTrackExpenseUtil(action); const transactionUnit = transaction?.comment?.customUnit?.distanceUnit; const sortedRates = useMemo(() => Object.values(rates).sort((a, b) => localeCompare(a.name ?? '', b.name ?? '')), [rates, localeCompare]); @@ -109,7 +116,7 @@ function IOURequestStepDistanceRate({ const effectiveRateID = pendingRateID ?? currentRateID; const isSelected = effectiveRateID ? effectiveRateID === rate.customUnitRateID && !hasUnitMismatchForMovingTrackExpense - : DistanceRequestUtils.getDefaultMileageRate(policy)?.customUnitRateID === rate.customUnitRateID; + : DistanceRequestUtils.getDefaultMileageRate(policyForRates)?.customUnitRateID === rate.customUnitRateID; const rateForDisplay = DistanceRequestUtils.getFormattedRateValue(unit, rate.rate, isSelected ? transactionCurrency : rate.currency, translate, toLocaleDigit, getCurrencySymbol); return { text: rate.name ?? rateForDisplay, @@ -152,12 +159,12 @@ function IOURequestStepDistanceRate({ let taxRateExternalID; let taxValue; if (shouldShowTax) { - const policyCustomUnitRate = getDistanceRateCustomUnitRate(policy, customUnitRateID); - const defaultTaxCode = getDefaultTaxCode(policy, currentTransaction) ?? ''; + const policyCustomUnitRate = getDistanceRateCustomUnitRate(policyForRates, customUnitRateID); + const defaultTaxCode = getDefaultTaxCode(policyForRates, currentTransaction) ?? ''; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing taxRateExternalID = policyCustomUnitRate?.attributes?.taxRateExternalID || defaultTaxCode; - const taxableAmount = DistanceRequestUtils.getTaxableAmount(policy, customUnitRateID, getDistanceInMeters(currentTransaction, currentUnit)); - taxValue = taxRateExternalID ? getTaxValue(policy, currentTransaction, taxRateExternalID) : undefined; + const taxableAmount = DistanceRequestUtils.getTaxableAmount(policyForRates, customUnitRateID, getDistanceInMeters(currentTransaction, currentUnit)); + taxValue = taxRateExternalID ? getTaxValue(policyForRates, currentTransaction, taxRateExternalID) : undefined; taxAmount = convertToBackendAmount(calculateTaxAmount(taxValue, taxableAmount, getCurrencyDecimals(rates[customUnitRateID].currency))); setMoneyRequestTaxAmount(transactionID, taxAmount, shouldUseTransactionDraft(action)); setMoneyRequestTaxRate(transactionID, taxRateExternalID ?? null, shouldUseTransactionDraft(action)); @@ -167,12 +174,12 @@ function IOURequestStepDistanceRate({ if (currentRateID !== customUnitRateID || (isMovingTransactionFromTrackExpense && transactionUnit !== selectedRateUnit)) { // In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value if (isEditingSplit && transaction) { - setDraftSplitTransaction(transaction.transactionID, splitDraftTransaction, {customUnitRateID}, policy); + setDraftSplitTransaction(transaction.transactionID, splitDraftTransaction, {customUnitRateID}, policyForRates); navigateBack(); return; } - setMoneyRequestDistanceRate(transaction, customUnitRateID, policy, shouldUseTransactionDraft(action)); + setMoneyRequestDistanceRate(transaction, customUnitRateID, policyForRates, shouldUseTransactionDraft(action)); if (isEditing && transaction?.transactionID) { updateMoneyRequestDistanceRate({ @@ -181,7 +188,7 @@ function IOURequestStepDistanceRate({ parentReport, parentReportNextStep, rateID: customUnitRateID, - policy, + policy: policyForRates, policyTagList: policyTags, policyCategories, currentUserAccountIDParam,