Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {useCurrencyListActions} from '@hooks/useCurrencyList';

import {setMoneyRequestCategory, setMoneyRequestTag} from '@libs/actions/IOU/MoneyRequest';
import {insertTagIntoTransactionTagsString} from '@libs/IOUUtils';
import {getTag} from '@libs/TransactionUtils';
Expand Down Expand Up @@ -38,16 +40,17 @@ function FieldAutoSelector({
iouCategory,
isMovingTransactionFromTrackExpense,
}: FieldAutoSelectorProps) {
const {getCurrencyDecimals} = useCurrencyListActions();
// Auto select the category if there is only one enabled category and it is required
useEffect(() => {
const enabledCategories = Object.values(policyCategories ?? {}).filter((category) => category.enabled);
if (!transactionID || iouCategory || !shouldShowCategories || enabledCategories.length !== 1 || !isCategoryRequired) {
return;
}
setMoneyRequestCategory(transactionID, enabledCategories.at(0)?.name ?? '', policy, isMovingTransactionFromTrackExpense);
setMoneyRequestCategory(transactionID, enabledCategories.at(0)?.name ?? '', policy, getCurrencyDecimals, 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]);
}, [shouldShowCategories, policyCategories, isCategoryRequired, policy?.id, getCurrencyDecimals]);

// Auto select the tag if there is only one enabled tag and it is required
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function useSplitParticipants({
}: UseSplitParticipantsParams) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {convertToDisplayString, convertToDisplayStringWithoutCurrency, getCurrencySymbol} = useCurrencyListActions();
const {convertToDisplayString, convertToDisplayStringWithoutCurrency, getCurrencySymbol, getCurrencyDecimals} = useCurrencyListActions();

const transactionID = transaction?.transactionID;
const onSplitShareChange = (accountID: number, value: number) => {
Expand All @@ -94,7 +94,7 @@ function useSplitParticipants({
if (iouAmount > 0) {
amount =
transaction?.comment?.splits?.find((split) => split.accountID === participantOption.accountID)?.amount ??
calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode ?? '', isPayer);
calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode ?? '', isPayer, false, getCurrencyDecimals);
}
return {
...participantOption,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ function AmountField({

const isPayer = splitShareAccountID === accountID;
acc[splitShareAccountID] = {
amount: calculateAmount(participantsLength, updatedAmount, updatedCurrency, isPayer),
amount: calculateAmount(participantsLength, updatedAmount, updatedCurrency, isPayer, false, getCurrencyDecimals),
isModified: false,
};
return acc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import DatePicker from '@components/DatePicker';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import {useConfirmationFields} from '@components/MoneyRequestConfirmationFields/context';

import {useCurrencyListActions} from '@hooks/useCurrencyList';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import usePersonalPolicy from '@hooks/usePersonalPolicy';
Expand Down Expand Up @@ -56,6 +57,7 @@ function DateField({
reportID,
reportActionID,
}: DateFieldProps) {
const {getCurrencyDecimals} = useCurrencyListActions();
const {isEditingSplitBill} = useConfirmationFields();
const styles = useThemeStyles();
const {translate} = useLocalize();
Expand Down Expand Up @@ -113,6 +115,7 @@ function DateField({
lastSelectedDistanceRates,
isDraft: shouldUseTransactionDraft(action),
personalPolicyOutputCurrency: personalPolicy?.outputCurrency,
getCurrencyDecimals,
});
}
};
Expand Down
17 changes: 13 additions & 4 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ function MoneyRequestView({
const {isOffline} = useNetwork();
const {environmentURL, isProduction} = useEnvironment();
const {translate, toLocaleDigit, localeCompare} = useLocalize();
const {convertToDisplayString, getCurrencySymbol} = useCurrencyListActions();
const {convertToDisplayString, getCurrencySymbol, getCurrencyDecimals} = useCurrencyListActions();
const {getReportRHPActiveRoute} = useActiveRoute();
const {showConfirmModal} = useConfirmModal();
const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH);
Expand Down Expand Up @@ -887,6 +887,7 @@ function MoneyRequestView({
delegateAccountID,
reportPolicyTags,
isTrackIntentUser,
getCurrencyDecimals,
});
});
};
Expand Down Expand Up @@ -1208,9 +1209,17 @@ function MoneyRequestView({
}

if (shouldShowSplitIndicator && isSplitAvailable) {
initSplitExpense(transaction, transactionThreadReport, splitEffectivePolicy, selfDMReportID, restrictedActionPolicyID, personalPolicy?.outputCurrency, {
isProduction,
});
initSplitExpense(
transaction,
transactionThreadReport,
splitEffectivePolicy,
selfDMReportID,
restrictedActionPolicyID,
personalPolicy?.outputCurrency,
{isProduction},
getCurrencyDecimals,
getCurrencySymbol,
);
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function TransactionPreviewContent({
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
const {convertToDisplayString} = useCurrencyListActions();
const {convertToDisplayString, getCurrencyDecimals} = useCurrencyListActions();
const {environmentURL} = useEnvironment();
const isParentPolicyExpenseChat = isPolicyExpenseChat(chatReport);
const transactionDetails = useMemo<Partial<TransactionDetails>>(
Expand Down Expand Up @@ -241,7 +241,14 @@ function TransactionPreviewContent({
}
}

return calculateAmount(isParentPolicyExpenseChat ? 1 : originalParticipantCount - 1, amount ?? 0, requestCurrency ?? '', actorAccountID === sessionAccountID);
return calculateAmount(
isParentPolicyExpenseChat ? 1 : originalParticipantCount - 1,
amount ?? 0,
requestCurrency ?? '',
actorAccountID === sessionAccountID,
false,
getCurrencyDecimals,
);
}, [
shouldShowSplitShare,
isParentPolicyExpenseChat,
Expand All @@ -253,6 +260,7 @@ function TransactionPreviewContent({
isBillSplit,
action,
actorAccountID,
getCurrencyDecimals,
]);

const shouldWrapDisplayAmount = !(isBillSplit || shouldShowMerchantOrDescription || isTransactionScanning);
Expand Down
4 changes: 2 additions & 2 deletions src/components/TransactionItemRow/DataCells/TotalCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';

import {convertToBackendAmount, convertToFrontendAmountAsString, getCurrencyDecimals, sanitizeCurrencyCode} from '@libs/CurrencyUtils';
import {convertToBackendAmount, convertToFrontendAmountAsString, sanitizeCurrencyCode} from '@libs/CurrencyUtils';
import {formatToParts} from '@libs/NumberFormatUtils';
import {parseFloatAnyLocale, roundToTwoDecimalPlaces} from '@libs/NumberUtils';
import {isGroupPolicy} from '@libs/PolicyUtils';
Expand Down Expand Up @@ -46,7 +46,7 @@ function getTransactionItemIouType(transactionItem: TransactionItem) {
function TotalCell({shouldShowTooltip, transactionItem, canEdit, onSave, report, policy}: TotalCellProps) {
const styles = useThemeStyles();
const {translate, preferredLocale} = useLocalize();
const {convertToDisplayString} = useCurrencyListActions();
const {convertToDisplayString, getCurrencyDecimals} = useCurrencyListActions();
const currency = getTransactionCurrency(transactionItem);

const effectiveReport = report ?? transactionItem.report;
Expand Down
19 changes: 16 additions & 3 deletions src/hooks/useDeleteTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import {isTrackIntentUserSelector} from '@selectors/Onboarding';
import passthroughPolicyTagListSelector from '@selectors/PolicyTagList';
import {useCallback} from 'react';

import type {CurrencyListActionsContextType} from './useCurrencyList';

import {useCurrencyListActions} from './useCurrencyList';
import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails';
import useDelegateAccountID from './useDelegateAccountID';
import useEnvironment from './useEnvironment';
Expand Down Expand Up @@ -58,12 +61,17 @@ type DeleteTransactionsResult =
deletedTransactionThreadReportIDs: string[];
};

function redistributeRemainingPerDiemSplitExpenses(splitExpenses: SplitExpense[], total: number, currency: string): SplitExpense[] {
function redistributeRemainingPerDiemSplitExpenses(
splitExpenses: SplitExpense[],
total: number,
currency: string,
getCurrencyDecimals: CurrencyListActionsContextType['getCurrencyDecimals'],
): SplitExpense[] {
const lastSplitIndex = splitExpenses.length - 1;

return splitExpenses.map((splitExpense, index) => ({
...splitExpense,
amount: calculateIOUAmount(lastSplitIndex, total, currency, index === lastSplitIndex, true),
amount: calculateIOUAmount(lastSplitIndex, total, currency, index === lastSplitIndex, true, getCurrencyDecimals),
}));
}

Expand All @@ -72,6 +80,7 @@ function redistributeRemainingPerDiemSplitExpenses(splitExpenses: SplitExpense[]
* All data must be provided through function parameters
*/
function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransactionsParams) {
const {getCurrencyDecimals, getCurrencySymbol} = useCurrencyListActions();
const {currentSearchResults} = useSearchResultsContext();
const {currentSearchQueryJSON} = useSearchQueryContext();
const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION);
Expand Down Expand Up @@ -191,6 +200,8 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac
navigateToEditSplitExpense: true,
isProduction,
},
getCurrencyDecimals,
getCurrencySymbol,
);
return {
action: 'redirected',
Expand Down Expand Up @@ -283,7 +294,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac
const remainingSplitExpensesTotal = remainingSplitExpenses.reduce((total, splitExpense) => total + splitExpense.amount, 0);
const updatedRemainingSplitExpenses =
originalTransaction && isPerDiemRequestTransactionUtils(originalTransaction) && remainingSplitExpenses.length > 0 && remainingSplitExpensesTotal !== splitExpensesTotal
? redistributeRemainingPerDiemSplitExpenses(remainingSplitExpenses, splitExpensesTotal, originalTransaction.currency ?? CONST.CURRENCY.USD)
? redistributeRemainingPerDiemSplitExpenses(remainingSplitExpenses, splitExpensesTotal, originalTransaction.currency ?? CONST.CURRENCY.USD, getCurrencyDecimals)
: remainingSplitExpenses;

const parentTransactionReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`];
Expand Down Expand Up @@ -402,6 +413,8 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac
personalPolicy?.outputCurrency,
delegateAccountID,
isTrackIntentUser,
getCurrencyDecimals,
getCurrencySymbol,
],
);

Expand Down
4 changes: 3 additions & 1 deletion src/hooks/useExpensifyCardRulesList.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {filterInactiveCards, getCardDescriptionForSearchTable, getSelectedCardsSharedCurrency} from '@libs/CardUtils';
import {convertToBackendAmount, convertToDisplayString} from '@libs/CurrencyUtils';
import {convertToBackendAmount} from '@libs/CurrencyUtils';
import {temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import {getSpendRuleFormValuesFromCardRule, getSpendRuleSummaryParts, getTruncatedSpendRuleSummary} from '@libs/SpendRulesUtils';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';

import {useCurrencyListActions} from './useCurrencyList';
import useDefaultFundID from './useDefaultFundID';
import useLocalize from './useLocalize';
import useNetwork from './useNetwork';
Expand All @@ -16,6 +17,7 @@ export default function useExpensifyCardRules(policyID: string) {
const {isOffline} = useNetwork();
const defaultFundID = useDefaultFundID(policyID);
const {translate, localeCompare} = useLocalize();
const {convertToDisplayString} = useCurrencyListActions();

const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [expensifyCardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`);
Expand Down
10 changes: 9 additions & 1 deletion src/hooks/useParticipantSubmission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import type {OnyxEntry} from 'react-native-onyx';

import {useEffect, useRef} from 'react';

import {useCurrencyListActions} from './useCurrencyList';
import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails';
import useLocalize from './useLocalize';
import useMappedPolicies from './useMappedPolicies';
Expand Down Expand Up @@ -79,6 +80,7 @@ function useParticipantSubmission({
isMovingTransactionFromTrackExpense,
isFocused,
}: UseParticipantSubmissionParams) {
const {getCurrencyDecimals} = useCurrencyListActions();
const {translate} = useLocalize();
const personalPolicy = usePersonalPolicy();

Expand Down Expand Up @@ -352,7 +354,13 @@ function useParticipantSubmission({
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, isMovingTransactionFromTrackExpense ? movingPolicy : undefined, isMovingTransactionFromTrackExpense);
setMoneyRequestCategory(
transaction.transactionID,
category,
isMovingTransactionFromTrackExpense ? movingPolicy : undefined,
getCurrencyDecimals,
isMovingTransactionFromTrackExpense,
);

if (shouldUpdateTransactionReportID) {
setTransactionReport(transaction.transactionID, {reportID: transactionReportID}, true);
Expand Down
4 changes: 4 additions & 0 deletions src/hooks/useTransactionInlineEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {useCallback, useRef} from 'react';
// eslint-disable-next-line no-restricted-imports -- Need original useOnyx to avoid reading partial Search snapshot policy data.
import {useOnyx as originalUseOnyx} from 'react-native-onyx';

import {useCurrencyListActions} from './useCurrencyList';
import useDelegateAccountID from './useDelegateAccountID';
import useDistanceRateOriginalPolicy from './useDistanceRateOriginalPolicy';
import useNetwork from './useNetwork';
Expand Down Expand Up @@ -81,6 +82,7 @@ type UseTransactionInlineEditReturn = {
};

function useTransactionInlineEdit({transactionID, hash, linkedReportAction}: UseTransactionInlineEditParams): UseTransactionInlineEditReturn {
const {getCurrencyDecimals, getCurrencySymbol} = useCurrencyListActions();
const delegateAccountID = useDelegateAccountID();
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`);

Expand Down Expand Up @@ -195,6 +197,8 @@ function useTransactionInlineEdit({transactionID, hash, linkedReportAction}: Use
personalDetailsList,
delegateAccountID,
isTrackIntentUser,
getCurrencyDecimals,
getCurrencySymbol,
};
};

Expand Down
14 changes: 12 additions & 2 deletions src/libs/IOUUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type {CurrencyListActionsContextType} from '@hooks/useCurrencyList';

import type {IOUAction, IOURequestType, IOUType} from '@src/CONST';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
Expand Down Expand Up @@ -73,11 +75,19 @@ function navigateToParticipantPage(iouType: ValueOf<typeof CONST.IOU.TYPE>, tran
* @param currency - Used to know how many decimal places are valid when splitting the total
* @param isDefaultUser - Whether we are calculating the amount for the remainder holder
* @param useFloorToLastRounding - `false` (default, legacy behavior) or `true` to floor all and put full remainder on the default user
* @param getCurrencyDecimals - Currency lookup supplied by React consumers. Falls back during staged migration.
*/
function calculateAmount(numberOfSplits: number, total: number, currency: string, isDefaultUser = false, useFloorToLastRounding = false): number {
function calculateAmount(
numberOfSplits: number,
total: number,
currency: string,
isDefaultUser = false,
useFloorToLastRounding = false,
getCurrencyDecimals?: CurrencyListActionsContextType['getCurrencyDecimals'],
): number {
// Since the backend can maximum store 2 decimal places, any currency with more than 2 decimals
// has to be capped to 2 decimal places
const currencyUnit = Math.min(100, getCurrencyUnit(currency));
const currencyUnit = Math.min(100, getCurrencyDecimals ? 10 ** getCurrencyDecimals(currency) : getCurrencyUnit(currency));
const totalInCurrencySubunit = (total / 100) * currencyUnit;
const totalParticipants = numberOfSplits + 1;

Expand Down
6 changes: 5 additions & 1 deletion src/libs/ModifiedExpenseMessage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type {LocalizedTranslate} from '@components/LocaleContextProvider';

import type {CurrencyListActionsContextType} from '@hooks/useCurrencyList';

import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {Policy, PolicyCategories, PolicyTagLists, Report, ReportAction, ReportAttributesDerivedValue} from '@src/types/onyx';
Expand All @@ -12,7 +14,7 @@ import type {Entries, ValueOf} from 'type-fest';
import isEmpty from 'lodash/isEmpty';

import {getDecodedCategoryName, isCategoryMissing} from './CategoryUtils';
import {convertToDisplayString} from './CurrencyUtils';
import {convertToDisplayString as convertToDisplayStringCurrencyUtils} from './CurrencyUtils';
import DateUtils from './DateUtils';
import {getEnvironmentURL} from './Environment/Environment';
import {formatList} from './Localize';
Expand Down Expand Up @@ -277,6 +279,7 @@ function getForReportAction({
policyCategories,
currentUserLogin,
reportAttributes,
convertToDisplayString = convertToDisplayStringCurrencyUtils,
}: {
translate: LocalizedTranslate;
reportAction: OnyxEntry<ReportAction>;
Expand All @@ -290,6 +293,7 @@ function getForReportAction({
policyCategories?: OnyxEntry<PolicyCategories>;
currentUserLogin: string;
reportAttributes?: ReportAttributesDerivedValue['reports'];
convertToDisplayString?: CurrencyListActionsContextType['convertToDisplayString'];
}): string {
if (!isModifiedExpenseAction(reportAction)) {
return '';
Expand Down
Loading
Loading