Skip to content
Merged
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
4 changes: 2 additions & 2 deletions __mocks__/reportData/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const transactionR14932: Transaction = {
reimbursable: true,
hasEReceipt: true,
cardID: 0,
modifiedAmount: '',
modifiedAmount: 0,
originalAmount: 0,
comment: {},
bank: '',
Expand Down Expand Up @@ -59,7 +59,7 @@ const transactionR98765: Transaction = {
hasEReceipt: true,
managedCard: false,
billable: false,
modifiedAmount: '',
modifiedAmount: 0,
cardID: 0,
originalAmount: 0,
comment: {},
Expand Down
1 change: 0 additions & 1 deletion src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,6 @@ const CONST = {
NO_OPTIMISTIC_TRANSACTION_THREADS: 'noOptimisticTransactionThreads',
UBER_FOR_BUSINESS: 'uberForBusiness',
CUSTOM_REPORT_NAMES: 'newExpensifyCustomReportNames',
ZERO_EXPENSES: 'zeroExpenses',
NEW_DOT_DEW: 'newDotDEW',
GPS_MILEAGE: 'gpsMileage',
},
Expand Down
1 change: 0 additions & 1 deletion src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1741,7 +1741,6 @@ function MoneyReportHeader({
paymentType={paymentType}
chatReport={chatReport}
moneyRequestReport={moneyRequestReport}
hasNonHeldExpenses={!hasOnlyHeldExpenses}
startAnimation={() => {
if (requestType === CONST.IOU.REPORT_ACTION_TYPE.APPROVE) {
startApprovedAnimation();
Expand Down
7 changes: 2 additions & 5 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -544,12 +544,9 @@ function MoneyRequestConfirmationList({
if (iouAmount !== 0) {
text = translate('iou.createExpenseWithAmount', {amount: formattedAmount});
}
} else if (isTypeSplit) {
text = translate('iou.splitAmount', {amount: formattedAmount});
} else if (iouAmount === 0) {
text = translate('iou.createExpense');
} else {
text = translate('iou.createExpenseWithAmount', {amount: formattedAmount});
const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.createExpenseWithAmount';
text = translate(translationKey, {amount: formattedAmount});
}
return [
{
Expand Down
12 changes: 4 additions & 8 deletions src/components/ProcessMoneyReportHoldMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,11 @@ type ProcessMoneyReportHoldMenuProps = {

/** Callback for displaying payment animation on IOU preview component */
startAnimation?: () => void;

/** Whether the report has non held expenses */
hasNonHeldExpenses?: boolean;
};

function ProcessMoneyReportHoldMenu({
requestType,
nonHeldAmount = '0',
nonHeldAmount,
fullAmount,
onClose,
isVisible,
Expand All @@ -63,7 +60,6 @@ function ProcessMoneyReportHoldMenu({
moneyRequestReport,
transactionCount,
startAnimation,
hasNonHeldExpenses,
}: ProcessMoneyReportHoldMenuProps) {
const {translate} = useLocalize();
const isApprove = requestType === CONST.IOU.REPORT_ACTION_TYPE.APPROVE;
Expand Down Expand Up @@ -106,19 +102,19 @@ function ProcessMoneyReportHoldMenu({
};

const promptText = useMemo(() => {
if (hasNonHeldExpenses) {
if (nonHeldAmount) {
return translate(isApprove ? 'iou.confirmApprovalAmount' : 'iou.confirmPayAmount');
}
return translate(isApprove ? 'iou.confirmApprovalAllHoldAmount' : 'iou.confirmPayAllHoldAmount', {count: transactionCount});
}, [hasNonHeldExpenses, transactionCount, translate, isApprove]);
}, [nonHeldAmount, transactionCount, translate, isApprove]);

return (
<DecisionModal
title={translate(isApprove ? 'iou.confirmApprove' : 'iou.confirmPay')}
onClose={onClose}
isVisible={isVisible}
prompt={promptText}
firstOptionText={hasNonHeldExpenses ? `${translate(isApprove ? 'iou.approveOnly' : 'iou.payOnly')} ${nonHeldAmount}` : undefined}
firstOptionText={nonHeldAmount ? `${translate(isApprove ? 'iou.approveOnly' : 'iou.payOnly')} ${nonHeldAmount}` : undefined}
secondOptionText={`${translate(isApprove ? 'iou.approve' : 'iou.pay')} ${fullAmount}`}
onFirstOptionSubmit={() => onSubmit(false)}
onSecondOptionSubmit={() => onSubmit(true)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function MoneyRequestReportPreview({
}

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return transactions.some((transaction) => (Number(transaction?.modifiedAmount) || transaction?.amount) < 0);
return transactions.some((transaction) => (transaction?.modifiedAmount || transaction?.amount) < 0);
}, [transactions, action.childType, iouReport]);

const openReportFromPreview = useCallback(() => {
Expand Down
20 changes: 7 additions & 13 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,6 @@ function MoneyRequestView({
const currentUserEmailParam = currentUserPersonalDetails.login ?? '';
const {isBetaEnabled} = usePermissions();
const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT);
const isZeroExpensesBetaEnabled = isBetaEnabled(CONST.BETAS.ZERO_EXPENSES);

const moneyRequestReport = parentReport;
const isApproved = isReportApproved({report: moneyRequestReport});
Expand Down Expand Up @@ -273,9 +272,7 @@ function MoneyRequestView({
originalCurrency: transactionOriginalCurrency,
postedDate: transactionPostedDate,
} = getTransactionDetails(transaction, undefined, undefined, allowNegativeAmount, false, currentUserPersonalDetails) ?? {};
const isZeroTransactionAmount = transactionAmount === 0;
const isEmptyMerchant =
transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transactionMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT;
const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
const isDistanceRequest = isDistanceRequestTransactionUtils(transaction);
const isManualDistanceRequest = isManualDistanceRequestTransactionUtils(transaction, !!mergeTransactionID);
const isMapDistanceRequest = isDistanceRequest && !isManualDistanceRequest;
Expand All @@ -287,10 +284,9 @@ function MoneyRequestView({
// Use the updated transaction amount in merge flow to have correct positive/negative sign
const actualAmount = isFromMergeTransaction && updatedTransaction ? updatedTransaction.amount : transactionAmount;
const actualCurrency = updatedTransaction ? getCurrency(updatedTransaction) : transactionCurrency;
const shouldDisplayTransactionAmount = (isDistanceRequest && hasRoute) || !isDistanceRequest;
const shouldDisplayTransactionAmount = ((isDistanceRequest && hasRoute) || !!actualAmount) && actualAmount !== undefined;
const formattedTransactionAmount = shouldDisplayTransactionAmount ? convertToDisplayString(actualAmount, actualCurrency) : '';
const formattedPerAttendeeAmount =
shouldDisplayTransactionAmount && actualAmount !== undefined ? convertToDisplayString(actualAmount / (transactionAttendees?.length ?? 1), actualCurrency) : '';
const formattedPerAttendeeAmount = shouldDisplayTransactionAmount ? convertToDisplayString(actualAmount / (actualAttendees?.length ?? 1), actualCurrency) : '';

const transactionOriginalAmount = transaction && getOriginalAmountForDisplay(transaction, isExpenseReport(moneyRequestReport));
const formattedOriginalAmount = transactionOriginalAmount && transactionOriginalCurrency && convertToDisplayString(transactionOriginalAmount, transactionOriginalCurrency);
Expand Down Expand Up @@ -396,18 +392,16 @@ function MoneyRequestView({
let rateToDisplay = isCustomUnitOutOfPolicy ? translate('common.rateOutOfPolicy') : DistanceRequestUtils.getRateForDisplay(unit, rate, currency, translate, toLocaleDigit, isOffline);
const distanceToDisplay = DistanceRequestUtils.getDistanceForDisplay(hasRoute, distance, unit, rate, translate);
let merchantTitle = isEmptyMerchant ? '' : transactionMerchant;
let amountTitle = formattedTransactionAmount?.toString() || '';
let amountTitle = formattedTransactionAmount ? formattedTransactionAmount.toString() : '';
if (isTransactionScanning) {
merchantTitle = translate('iou.receiptStatusTitle');
amountTitle = translate('iou.receiptStatusTitle');
}

const shouldNavigateToUpgradePath = !policyForMovingExpenses && !shouldSelectPolicy;

const updatedTransactionDescription = getDescription(updatedTransaction) || undefined;
const isEmptyUpdatedMerchant =
updatedTransaction?.modifiedMerchant === '' ||
updatedTransaction?.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT ||
updatedTransaction?.modifiedMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT;
const isEmptyUpdatedMerchant = updatedTransaction?.modifiedMerchant === '' || updatedTransaction?.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
const updatedMerchantTitle = isEmptyUpdatedMerchant ? '' : (updatedTransaction?.modifiedMerchant ?? merchantTitle);

const saveBillable = (newBillable: boolean) => {
Expand Down Expand Up @@ -483,7 +477,7 @@ function MoneyRequestView({
// NOTE: receipt field can return multiple violations, so we need to handle it separately
const fieldChecks: Partial<Record<ViolationField, {isError: boolean; translationPath: TranslationPaths}>> = {
amount: {
isError: isZeroTransactionAmount && !isZeroExpensesBetaEnabled,
isError: transactionAmount === 0,
translationPath: canEditAmount ? 'common.error.enterAmount' : 'common.error.missingAmount',
},
merchant: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function TransactionPreview(props: TransactionPreviewProps) {

// See description of `transactionRawAmount` prop for more context
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const transactionRawAmount = (Number(transaction?.modifiedAmount) || transaction?.amount) ?? 0;
const transactionRawAmount = (transaction?.modifiedAmount || transaction?.amount) ?? 0;

const shouldDisableOnPress = isBillSplit && isEmptyObject(transaction);
const isTransactionMadeWithCard = isManagedCardTransaction(transaction);
Expand Down
11 changes: 6 additions & 5 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ import {
shouldShowYear as shouldShowYearUtil,
} from '@libs/SearchUIUtils';
import {cancelSpan, endSpan, startSpan} from '@libs/telemetry/activeSpans';
import {getOriginalTransactionWithSplitInfo, hasValidModifiedAmount, isOnHold, isTransactionPendingDelete, mergeProhibitedViolations, shouldShowViolation} from '@libs/TransactionUtils';
import {getOriginalTransactionWithSplitInfo, isOnHold, isTransactionPendingDelete, mergeProhibitedViolations, shouldShowViolation} from '@libs/TransactionUtils';
import Navigation, {navigationRef} from '@navigation/Navigation';
import type {SearchFullscreenNavigatorParamList} from '@navigation/types';
import EmptySearchView from '@pages/Search/EmptySearchView';
Expand Down Expand Up @@ -126,7 +126,7 @@ function mapTransactionItemToSelectedEntry(
groupExchangeRate: item.groupExchangeRate,
reportID: item.reportID,
policyID: item.report?.policyID,
amount: hasValidModifiedAmount(item) ? Number(item.modifiedAmount) : item.amount,
amount: item.modifiedAmount ?? item.amount,
Comment on lines 127 to +129

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fall back to original amount when modifiedAmount is 0

Using item.modifiedAmount ?? item.amount treats an explicitly set modifiedAmount of 0 as valid, but the rest of the app uses 0 as the “unset/missing” sentinel (e.g., other views use modifiedAmount || amount). When a transaction has a real non‑zero amount and modifiedAmount is stored as 0 (common for unmodified transactions), this selection entry will carry amount = 0, which then flows into selected report totals and bulk actions/export. The bug appears whenever modifiedAmount is explicitly 0, causing totals for selected items to be wrong.

Useful? React with 👍 / 👎.

groupAmount: item.groupAmount,
currency: item.currency,
isFromOneTransactionReport: isOneTransactionReport(item.report),
Expand Down Expand Up @@ -176,7 +176,8 @@ function prepareTransactionsList(
action: item.action,
reportID: item.reportID,
policyID: item.policyID,
amount: Math.abs(hasValidModifiedAmount(item) ? Number(item.modifiedAmount) : item.amount),
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
amount: Math.abs(item.modifiedAmount || item.amount),
groupAmount: item.groupAmount,
groupCurrency: item.groupCurrency,
groupExchangeRate: item.groupExchangeRate,
Expand Down Expand Up @@ -536,7 +537,7 @@ function Search({
canReject: canRejectRequest,
reportID: transactionItem.reportID,
policyID: transactionItem.report?.policyID,
amount: hasValidModifiedAmount(transactionItem) ? Number(transactionItem.modifiedAmount) : transactionItem.amount,
amount: transactionItem.modifiedAmount ?? transactionItem.amount,
groupAmount: transactionItem.groupAmount,
groupCurrency: transactionItem.groupCurrency,
groupExchangeRate: transactionItem.groupExchangeRate,
Expand Down Expand Up @@ -589,7 +590,7 @@ function Search({
canReject: canRejectRequest,
reportID: transactionItem.reportID,
policyID: transactionItem.report?.policyID,
amount: hasValidModifiedAmount(transactionItem) ? Number(transactionItem.modifiedAmount) : transactionItem.amount,
amount: transactionItem.modifiedAmount ?? transactionItem.amount,
groupAmount: transactionItem.groupAmount,
groupCurrency: transactionItem.groupCurrency,
groupExchangeRate: transactionItem.groupExchangeRate,
Expand Down
2 changes: 1 addition & 1 deletion src/components/TransactionItemRow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function getMerchantName(transactionItem: TransactionWithOptionalSearchFields, t
}

const merchantName = StringUtils.getFirstLine(merchant);
return merchantName !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && merchantName !== CONST.TRANSACTION.DEFAULT_MERCHANT ? merchantName : '';
return merchantName !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT ? merchantName : '';
}

function TransactionItemRow({
Expand Down
1 change: 0 additions & 1 deletion src/libs/API/parameters/TrackExpenseParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ type TrackExpenseParams = {
customUnitRateID?: string;
description?: string;
distance?: number;
isDistance?: boolean;
};

export default TrackExpenseParams;
6 changes: 1 addition & 5 deletions src/libs/DistanceRequestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import type Policy from '@src/types/onyx/Policy';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {getCurrencySymbol} from './CurrencyUtils';
// This will be fixed as part of https://github.com/Expensify/App/issues/66397
// eslint-disable-next-line @typescript-eslint/no-deprecated
import {getDistanceRateCustomUnit, getDistanceRateCustomUnitRate, getPersonalPolicy, getUnitRateValue} from './PolicyUtils';
import {getCurrency, getRateID, isCustomUnitRateIDForP2P} from './TransactionUtils';

Expand Down Expand Up @@ -177,7 +175,7 @@
translate: LocaleContextProps['translate'],
useShortFormUnit?: boolean,
): string {
if (!hasRoute || !unit) {
if (!hasRoute || !unit || !distanceInMeters) {
return translate('iou.fieldPending');
}

Expand Down Expand Up @@ -371,9 +369,7 @@
if (isEmptyObject(mileageRates) && policyDraft) {
mileageRates = getMileageRates(policyDraft, true, transaction?.comment?.customUnit?.customUnitRateID);
}
// This will be fixed as part of https://github.com/Expensify/App/issues/66397
// eslint-disable-next-line @typescript-eslint/no-deprecated
const policyCurrency = policy?.outputCurrency ?? getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD;

Check failure on line 372 in src/libs/DistanceRequestUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

`getPersonalPolicy` is deprecated. Please use ONYXKEYS.PERSONAL_POLICY_ID to find the personal policyID

Check failure on line 372 in src/libs/DistanceRequestUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

`getPersonalPolicy` is deprecated. Please use ONYXKEYS.PERSONAL_POLICY_ID to find the personal policyID
const defaultMileageRate = getDefaultMileageRate(policy);
const customUnitRateID = getRateID(transaction);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
Expand Down
8 changes: 4 additions & 4 deletions src/libs/ModifiedExpenseMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ function getForReportAction({
if (hasModifiedAmount) {
const oldCurrency = reportActionOriginalMessage?.oldCurrency;
const oldAmountValue = reportActionOriginalMessage?.oldAmount ?? 0;
const oldAmount = convertToDisplayString(oldAmountValue, oldCurrency);
const oldAmount = oldAmountValue ? convertToDisplayString(reportActionOriginalMessage?.oldAmount ?? 0, oldCurrency) : '';

const currency = reportActionOriginalMessage?.currency;
const amount = convertToDisplayString(reportActionOriginalMessage?.amount ?? 0, currency);
Expand Down Expand Up @@ -397,7 +397,7 @@ function getForReportAction({

const taxAmount = convertToDisplayString(getTaxAmountAbsValue(reportActionOriginalMessage?.taxAmount ?? 0), currency);
const oldTaxAmountValue = getTaxAmountAbsValue(reportActionOriginalMessage?.oldTaxAmount ?? 0);
const oldTaxAmount = convertToDisplayString(oldTaxAmountValue, currency);
const oldTaxAmount = oldTaxAmountValue > 0 ? convertToDisplayString(oldTaxAmountValue, currency) : '';
// eslint-disable-next-line @typescript-eslint/no-deprecated
buildMessageFragmentForValue(translateLocal, taxAmount, oldTaxAmount, translateLocal('iou.taxAmount'), false, setFragments, removalFragments, changeFragments);
}
Expand Down Expand Up @@ -536,7 +536,7 @@ function getForReportActionTemp({
if (hasModifiedAmount) {
const oldCurrency = reportActionOriginalMessage?.oldCurrency;
const oldAmountValue = reportActionOriginalMessage?.oldAmount ?? 0;
const oldAmount = convertToDisplayString(oldAmountValue, oldCurrency);
const oldAmount = oldAmountValue ? convertToDisplayString(reportActionOriginalMessage?.oldAmount ?? 0, oldCurrency) : '';

const currency = reportActionOriginalMessage?.currency;
const amount = convertToDisplayString(reportActionOriginalMessage?.amount ?? 0, currency);
Expand Down Expand Up @@ -659,7 +659,7 @@ function getForReportActionTemp({

const taxAmount = convertToDisplayString(getTaxAmountAbsValue(reportActionOriginalMessage?.taxAmount ?? 0), currency);
const oldTaxAmountValue = getTaxAmountAbsValue(reportActionOriginalMessage?.oldTaxAmount ?? 0);
const oldTaxAmount = convertToDisplayString(oldTaxAmountValue, currency);
const oldTaxAmount = oldTaxAmountValue > 0 ? convertToDisplayString(oldTaxAmountValue, currency) : '';
buildMessageFragmentForValue(translate, taxAmount, oldTaxAmount, translate('iou.taxAmount'), false, setFragments, removalFragments, changeFragments);
}

Expand Down
11 changes: 4 additions & 7 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import type {MoneyRequestAmountInputProps} from '@components/MoneyRequestAmountInput';
import type {TransactionWithOptionalSearchFields} from '@components/TransactionItemRow';
import type PolicyData from '@hooks/usePolicyData/types';
import {computeReportName} from '@libs/ReportNameUtils';

Check warning on line 26 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unexpected subpath import via alias '@libs/ReportNameUtils'. Use './ReportNameUtils' instead
import type {PolicyTagList} from '@pages/workspace/tags/types';
import type {ThemeColors} from '@styles/theme/types';
import type {IOUAction, IOUType, OnboardingAccounting} from '@src/CONST';
Expand Down Expand Up @@ -5371,7 +5371,7 @@
originalMessage.oldMerchant = getMerchant(oldTransaction);

// For the originalMessage, we should use the non-negative amount, similar to what getAmount does for oldAmount
originalMessage.amount = Math.abs(Number(updatedTransaction?.modifiedAmount ?? 0));
originalMessage.amount = Math.abs(updatedTransaction?.modifiedAmount ?? 0);
originalMessage.currency = updatedTransaction?.modifiedCurrency ?? CONST.CURRENCY.USD;
originalMessage.merchant = updatedTransaction?.modifiedMerchant;
}
Expand Down Expand Up @@ -10635,10 +10635,10 @@
const adjustedTotal = total * coefficient;

// For the "approve unheld" option to be valid, we need:
// 1. There should be held expenses
// 1. There should be held expenses (unheld amount != total amount)
// 2. For expense reports with negative totals, we need to ensure the unheld amount is valid
// by checking that the absolute values are meaningful and different
const hasHeldExpensesLocal = hasHeldExpenses(iouReport?.reportID);
const hasHeldExpensesLocal = unheldTotal !== total;
const hasValidNonHeldAmount =
hasHeldExpensesLocal &&
// For normal cases (positive amounts or IOU reports)
Expand Down Expand Up @@ -12244,10 +12244,7 @@
function doesReportContainRequestsFromMultipleUsers(iouReport: OnyxEntry<Report>): boolean {
const transactions = getReportTransactions(iouReport?.reportID);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
if (Permissions.isBetaEnabled(CONST.BETAS.ZERO_EXPENSES, allBetas)) {
return isIOUReport(iouReport) && transactions.some((transaction) => (Number(transaction?.modifiedAmount) || transaction?.amount) <= 0);
}
return isIOUReport(iouReport) && transactions.some((transaction) => (Number(transaction?.modifiedAmount) || transaction?.amount) < 0);
return isIOUReport(iouReport) && transactions.some((transaction) => (transaction?.modifiedAmount || transaction?.amount) < 0);
}

/**
Expand Down
Loading
Loading