Skip to content

Commit 6e679a9

Browse files
committed
Updates
1 parent 4dae8fe commit 6e679a9

File tree

8 files changed

+54
-24
lines changed

8 files changed

+54
-24
lines changed

src/components/MoneyReportHeader.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {OnyxEntry} from 'react-native-onyx';
1010
import type {ValueOf} from 'type-fest';
1111
import useConfirmModal from '@hooks/useConfirmModal';
1212
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
13+
import {useCurrencyListState} from '@hooks/useCurrencyList';
1314
import useDefaultExpensePolicy from '@hooks/useDefaultExpensePolicy';
1415
import useDeleteTransactions from '@hooks/useDeleteTransactions';
1516
import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactionsAndViolations';
@@ -351,6 +352,7 @@ function MoneyReportHeader({
351352
const [originalTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transaction?.comment?.originalTransactionID)}`);
352353
const [allTransactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
353354
const [cardList] = useOnyx(ONYXKEYS.CARD_LIST);
355+
const {currencyList} = useCurrencyListState();
354356
const {isBetaEnabled} = usePermissions();
355357
const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT);
356358
const isDEWBetaEnabled = isBetaEnabled(CONST.BETAS.NEW_DOT_DEW);
@@ -919,7 +921,7 @@ function MoneyReportHeader({
919921
}, [connectedIntegration, exportModalStatus, moneyRequestReport?.reportID]);
920922

921923
const getAmount = (actionType: ValueOf<typeof CONST.REPORT.REPORT_PREVIEW_ACTIONS>) => ({
922-
formattedAmount: getTotalAmountForIOUReportPreviewButton(moneyRequestReport, policy, actionType),
924+
formattedAmount: getTotalAmountForIOUReportPreviewButton(moneyRequestReport, policy, actionType, currencyList),
923925
});
924926

925927
const {formattedAmount: totalAmount} = getAmount(CONST.REPORT.PRIMARY_ACTIONS.PAY);
@@ -1776,7 +1778,7 @@ function MoneyReportHeader({
17761778
}
17771779
return option;
17781780
});
1779-
}, [originalSelectedTransactionsOptions, showDeleteModal, dismissedRejectUseExplanation]);
1781+
}, [originalSelectedTransactionsOptions, showDeleteModal, dismissedRejectUseExplanation, isDelegateAccessRestricted, showDelegateNoAccessModal]);
17801782

17811783
const shouldShowSelectedTransactionsButton = !!selectedTransactionsOptions.length && !transactionThreadReportID;
17821784

src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {showContextMenuForReport} from '@components/ShowContextMenuContext';
2727
import Text from '@components/Text';
2828
import useConfirmModal from '@hooks/useConfirmModal';
2929
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
30+
import {useCurrencyListState} from '@hooks/useCurrencyList';
3031
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
3132
import useLocalize from '@hooks/useLocalize';
3233
import useNetwork from '@hooks/useNetwork';
@@ -182,6 +183,7 @@ function MoneyRequestReportPreviewContent({
182183
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
183184
const {isBetaEnabled} = usePermissions();
184185
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
186+
const {currencyList} = useCurrencyListState();
185187
const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST);
186188
const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT);
187189
const [betas] = useOnyx(ONYXKEYS.BETAS);
@@ -649,7 +651,7 @@ function MoneyRequestReportPreviewContent({
649651
);
650652

651653
const isReportDeleted = action?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
652-
const formattedAmount = getTotalAmountForIOUReportPreviewButton(iouReport, policy, reportPreviewAction);
654+
const formattedAmount = getTotalAmountForIOUReportPreviewButton(iouReport, policy, reportPreviewAction, currencyList);
653655

654656
const reportPreviewActions = {
655657
[CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT]: (

src/libs/CurrencyUtils.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ import {format, formatToParts} from './NumberFormatUtils';
1111
* @param currency - IOU currency
1212
*/
1313
function getCurrencyDecimals(currency: string = CONST.CURRENCY.USD, currencies?: CurrencyList): number {
14-
const decimals = currencies?.[currency]?.decimals;
14+
const normalizedCurrency = currency.toUpperCase();
15+
const decimals = currencies?.[normalizedCurrency]?.decimals;
16+
if (decimals !== undefined) {
17+
return decimals;
18+
}
19+
1520
return decimals ?? 2;
1621
}
1722

@@ -40,7 +45,7 @@ function getLocalizedCurrencySymbol(locale: Locale | undefined, currencyCode: st
4045
* Get the currency symbol for a currency(ISO 4217) Code
4146
*/
4247
function getCurrencySymbol(currencyCode: string, currencies?: CurrencyList): string | undefined {
43-
return currencies?.[currencyCode]?.symbol;
48+
return currencies?.[currencyCode.toUpperCase()]?.symbol;
4449
}
4550

4651
/**
@@ -188,7 +193,9 @@ function convertToDisplayStringWithoutCurrency(amountInCents: number, currency:
188193
* Checks if passed currency code is a valid currency based on currency list
189194
*/
190195
function isValidCurrencyCode(currencyCode: string, currencies?: CurrencyList): boolean {
191-
const currency = currencies?.[currencyCode];
196+
const normalizedCurrencyCode = currencyCode.toUpperCase();
197+
198+
const currency = currencies?.[normalizedCurrencyCode];
192199
return !!currency;
193200
}
194201

src/libs/Formula.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {endOfDay, endOfMonth, endOfWeek, getDay, lastDayOfMonth, set, startOfMon
22
import type {OnyxEntry} from 'react-native-onyx';
33
import type {ValueOf} from 'type-fest';
44
import CONST from '@src/CONST';
5-
import type {PersonalDetails, Policy, PolicyReportField, Report, Transaction} from '@src/types/onyx';
5+
import type {CurrencyList, PersonalDetails, Policy, PolicyReportField, Report, Transaction} from '@src/types/onyx';
66
import {isEmptyObject} from '@src/types/utils/EmptyObject';
77
import {convertToDisplayString, convertToDisplayStringWithoutCurrency, isValidCurrencyCode} from './CurrencyUtils';
88
import {formatDate} from './FormulaDatetime';
@@ -31,6 +31,7 @@ type MinimalTransaction = Pick<Transaction, 'transactionID' | 'reportID' | 'crea
3131
type FormulaContext = {
3232
report: Report;
3333
policy: OnyxEntry<Policy>;
34+
currencyList?: CurrencyList;
3435
transaction?: Transaction;
3536
submitterPersonalDetails?: PersonalDetails;
3637
managerPersonalDetails?: PersonalDetails;
@@ -370,12 +371,12 @@ function computeReportPart(part: FormulaPart, context: FormulaContext): string {
370371
case 'enddate':
371372
return formatDate(getNewestTransactionDate(report.reportID, context), format);
372373
case 'total': {
373-
const formattedAmount = formatAmount(report.total, report.currency, format);
374+
const formattedAmount = formatAmount(report.total, report.currency, format, context.currencyList);
374375
// Return empty string when conversion needed (formatAmount returns null for unavailable conversions)
375376
return formattedAmount ?? '';
376377
}
377378
case 'reimbursable': {
378-
const formattedAmount = formatAmount(getMoneyRequestSpendBreakdown(report).reimbursableSpend, report.currency, format);
379+
const formattedAmount = formatAmount(getMoneyRequestSpendBreakdown(report).reimbursableSpend, report.currency, format, context.currencyList);
379380
return formattedAmount ?? '';
380381
}
381382
case 'currency':
@@ -559,7 +560,7 @@ function getSubstring(value: string, args: string[]): string {
559560
* Format an amount value
560561
* @returns The formatted amount string, or null if currency conversion is needed (unavailable on frontend)
561562
*/
562-
function formatAmount(amount: number | undefined, currency: string | undefined, displayCurrency?: string): string | null {
563+
function formatAmount(amount: number | undefined, currency: string | undefined, displayCurrency?: string, currencyList?: CurrencyList): string | null {
563564
if (amount === undefined) {
564565
return '';
565566
}
@@ -570,11 +571,11 @@ function formatAmount(amount: number | undefined, currency: string | undefined,
570571
const trimmedDisplayCurrency = displayCurrency?.trim().toUpperCase();
571572
if (trimmedDisplayCurrency) {
572573
if (trimmedDisplayCurrency === 'NOSYMBOL') {
573-
return convertToDisplayStringWithoutCurrency(absoluteAmount, currency);
574+
return convertToDisplayStringWithoutCurrency(absoluteAmount, currency, currencyList);
574575
}
575576

576577
// Check if format is a valid currency code (e.g., USD, EUR, eur)
577-
if (!isValidCurrencyCode(trimmedDisplayCurrency)) {
578+
if (!isValidCurrencyCode(trimmedDisplayCurrency, currencyList)) {
578579
return '';
579580
}
580581

@@ -585,14 +586,14 @@ function formatAmount(amount: number | undefined, currency: string | undefined,
585586
return null;
586587
}
587588

588-
return convertToDisplayString(absoluteAmount, trimmedDisplayCurrency);
589+
return convertToDisplayString(absoluteAmount, trimmedDisplayCurrency, false, currencyList);
589590
}
590591

591-
if (currency && isValidCurrencyCode(currency)) {
592-
return convertToDisplayString(absoluteAmount, currency, true);
592+
if (currency && isValidCurrencyCode(currency, currencyList)) {
593+
return convertToDisplayString(absoluteAmount, currency, true, currencyList);
593594
}
594595

595-
return convertToDisplayStringWithoutCurrency(absoluteAmount, currency);
596+
return convertToDisplayStringWithoutCurrency(absoluteAmount, currency, currencyList);
596597
} catch (error) {
597598
Log.hmmm('[Formula] formatAmount failed', {error, amount, currency, displayCurrency});
598599
return '';

src/libs/MoneyRequestReportUtils.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
22
import type {ValueOf} from 'type-fest';
33
import type {TransactionListItemType} from '@components/SelectionListWithSections/types';
44
import CONST from '@src/CONST';
5-
import type {OriginalMessageIOU, Policy, Report, ReportAction, ReportMetadata, Transaction} from '@src/types/onyx';
5+
import type {CurrencyList, OriginalMessageIOU, Policy, Report, ReportAction, ReportMetadata, Transaction} from '@src/types/onyx';
66
import {convertToDisplayString} from './CurrencyUtils';
77
import {isPaidGroupPolicy} from './PolicyUtils';
88
import {getIOUActionForTransactionID, getOriginalMessage, isDeletedAction, isDeletedParentAction, isMoneyRequestAction} from './ReportActionsUtils';
@@ -152,7 +152,12 @@ function shouldWaitForTransactions(report: OnyxEntry<Report>, transactions: Tran
152152
* @param reportPreviewAction - The action that will take place when button is clicked which determines how amounts are calculated and displayed.
153153
* @returns - The total amount to be formatted as a string. Returns an empty string if no amount is applicable.
154154
*/
155-
const getTotalAmountForIOUReportPreviewButton = (report: OnyxEntry<Report>, policy: OnyxEntry<Policy>, reportPreviewAction: ValueOf<typeof CONST.REPORT.REPORT_PREVIEW_ACTIONS>) => {
155+
const getTotalAmountForIOUReportPreviewButton = (
156+
report: OnyxEntry<Report>,
157+
policy: OnyxEntry<Policy>,
158+
reportPreviewAction: ValueOf<typeof CONST.REPORT.REPORT_PREVIEW_ACTIONS>,
159+
currencyList?: CurrencyList,
160+
) => {
156161
// Determine whether the non-held amount is appropriate to display for the PAY button.
157162
const {nonHeldAmount, hasValidNonHeldAmount} = getNonHeldAndFullAmount(report, reportPreviewAction === CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY);
158163
const hasOnlyHeldExpenses = hasOnlyHeldExpensesReportUtils(report?.reportID);
@@ -173,11 +178,11 @@ const getTotalAmountForIOUReportPreviewButton = (report: OnyxEntry<Report>, poli
173178
}
174179

175180
// Default to reimbursable spend for PAY button if above conditions are not met.
176-
return convertToDisplayString(reimbursableSpend, report?.currency);
181+
return convertToDisplayString(reimbursableSpend, report?.currency, false, currencyList);
177182
}
178183

179184
// For all other cases, return the total display spend.
180-
return convertToDisplayString(totalDisplaySpend, report?.currency);
185+
return convertToDisplayString(totalDisplaySpend, report?.currency, false, currencyList);
181186
};
182187

183188
export {

src/libs/ReportUtils.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import type {
6262
TransactionViolation,
6363
TransactionViolations,
6464
VisibleReportActionsDerivedValue,
65+
CurrencyList,
6566
} from '@src/types/onyx';
6667
import type {ReportTransactionsAndViolations} from '@src/types/onyx/DerivedValues';
6768
import type {Attendee, Participant} from '@src/types/onyx/IOU';
@@ -974,6 +975,7 @@ type BuildOptimisticExpenseReportParams = {
974975
optimisticIOUReportID?: string;
975976
reportTransactions?: Record<string, Transaction>;
976977
createdTimestamp?: string;
978+
currencyList?: CurrencyList;
977979
};
978980

979981
type ReportByPolicyMap = Record<string, OnyxCollection<Report>>;
@@ -6632,7 +6634,13 @@ function buildOptimisticInvoiceReport(
66326634
/**
66336635
* Computes the optimistic report name using the policy's title field formula, with a fallback to the default expense report name.
66346636
*/
6635-
function computeOptimisticReportName(report: Report, policy: OnyxEntry<Policy>, policyID: string | undefined, reportTransactions: Record<string, Transaction>): string | null {
6637+
function computeOptimisticReportName(
6638+
report: Report,
6639+
policy: OnyxEntry<Policy>,
6640+
policyID: string | undefined,
6641+
reportTransactions: Record<string, Transaction>,
6642+
currencyList?: CurrencyList,
6643+
): string | null {
66366644
if (!isGroupPolicy(policy?.type ?? '')) {
66376645
return null;
66386646
}
@@ -6641,6 +6649,7 @@ function computeOptimisticReportName(report: Report, policy: OnyxEntry<Policy>,
66416649
const formulaContext: FormulaContext = {
66426650
report,
66436651
policy,
6652+
currencyList,
66446653
allTransactions: reportTransactions,
66456654
};
66466655

@@ -6713,13 +6722,14 @@ function buildOptimisticExpenseReport({
67136722
optimisticIOUReportID,
67146723
reportTransactions,
67156724
createdTimestamp,
6725+
currencyList,
67166726
}: BuildOptimisticExpenseReportParams): OptimisticExpenseReport {
67176727
// The amount for Expense reports are stored as negative value in the database
67186728
const storedTotal = total * -1;
67196729
const storedNonReimbursableTotal = nonReimbursableTotal * -1;
67206730
const report = chatReportID ? getReportOrDraftReport(chatReportID) : undefined;
67216731
const policyName = getPolicyName({report});
6722-
const formattedTotal = convertToDisplayString(storedTotal, currency);
6732+
const formattedTotal = convertToDisplayString(storedTotal, currency, false, currencyList);
67236733
// This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850
67246734
// eslint-disable-next-line @typescript-eslint/no-deprecated
67256735
const policyReal = getPolicy(policyID);
@@ -6763,7 +6773,7 @@ function buildOptimisticExpenseReport({
67636773
}
67646774

67656775
// Compute optimistic report name if applicable
6766-
const computedName = computeOptimisticReportName(expenseReport, policy, policyID, reportTransactions ?? {});
6776+
const computedName = computeOptimisticReportName(expenseReport, policy, policyID, reportTransactions ?? {}, currencyList);
67676777
if (computedName !== null) {
67686778
expenseReport.reportName = computedName;
67696779
}
@@ -6782,6 +6792,7 @@ function buildOptimisticEmptyReport(
67826792
policy: OnyxEntry<Policy>,
67836793
timeOfCreation: string,
67846794
betas: OnyxEntry<Beta[]>,
6795+
currencyList?: CurrencyList,
67856796
) {
67866797
const {stateNum, statusNum} = getExpenseReportStateAndStatus(policy, betas, true);
67876798
const optimisticEmptyReport: OptimisticNewReport = {
@@ -6806,7 +6817,7 @@ function buildOptimisticEmptyReport(
68066817
};
68076818

68086819
// Compute optimistic report name if applicable
6809-
const optimisticReportName = computeOptimisticReportName(optimisticEmptyReport as Report, policy, policy?.id, {});
6820+
const optimisticReportName = computeOptimisticReportName(optimisticEmptyReport as Report, policy, policy?.id, {}, currencyList);
68106821
if (optimisticReportName !== null) {
68116822
optimisticEmptyReport.reportName = optimisticReportName;
68126823
}

src/libs/actions/IOU/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,7 @@ function getAllReports(): OnyxCollection<OnyxTypes.Report> {
10281028
return allReports;
10291029
}
10301030

1031+
10311032
function getAllReportActionsFromIOU(): OnyxCollection<OnyxTypes.ReportActions> {
10321033
return allReportActions;
10331034
}

src/libs/actions/Report/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ Onyx.connect({
366366
},
367367
});
368368

369+
369370
const typingWatchTimers: Record<string, NodeJS.Timeout> = {};
370371

371372
let reportIDDeeplinkedFromOldDot: string | undefined;

0 commit comments

Comments
 (0)