From 93f26c98a7ea676adfe0fa224ffa350b702d322b Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 15 Oct 2025 11:13:04 +0200 Subject: [PATCH 01/65] Allow zero amount expenses round 2 --- .../ReportActionItem/MoneyRequestView.tsx | 19 ++++++------ src/libs/TransactionPreviewUtils.ts | 3 +- src/libs/TransactionUtils/index.ts | 3 +- src/pages/iou/MoneyRequestAmountForm.tsx | 2 +- .../step/IOURequestStepDistanceManual.tsx | 2 +- tests/unit/DebugUtilsTest.ts | 30 +++++++++++-------- tests/unit/ReportUtilsTest.ts | 6 ++-- 7 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 94415a58512e..add7a631bb22 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -67,6 +67,7 @@ import { getTagForDisplay, getTaxName, hasMissingSmartscanFields, + hasReceipt, hasReservationList, hasRoute as hasRouteTransactionUtils, isCardTransaction as isCardTransactionTransactionUtils, @@ -199,9 +200,15 @@ 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) || !!actualAmount) && actualAmount !== undefined; + const shouldDisplayTransactionAmount = useMemo(() => { + return ( + ((isDistanceRequest && hasRoute) || (!!actualAmount && hasReceipt(updatedTransaction ?? transaction)) || !hasReceipt(updatedTransaction ?? transaction)) && + actualAmount !== undefined + ); + }, [isDistanceRequest, hasRoute, actualAmount, updatedTransaction, transaction]); const formattedTransactionAmount = shouldDisplayTransactionAmount ? convertToDisplayString(actualAmount, actualCurrency) : ''; - const formattedPerAttendeeAmount = shouldDisplayTransactionAmount ? convertToDisplayString(actualAmount / (actualAttendees?.length ?? 1), actualCurrency) : ''; + const formattedPerAttendeeAmount = + shouldDisplayTransactionAmount && actualAmount !== undefined ? convertToDisplayString(actualAmount / (transactionAttendees?.length ?? 1), actualCurrency) : ''; const formattedOriginalAmount = transactionOriginalAmount && transactionOriginalCurrency && convertToDisplayString(transactionOriginalAmount, transactionOriginalCurrency); const isCardTransaction = isCardTransactionTransactionUtils(transaction); @@ -303,7 +310,7 @@ function MoneyRequestView({ const 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 ? formattedTransactionAmount.toString() : ''; + let amountTitle = formattedTransactionAmount?.toString() || ''; if (isTransactionScanning) { merchantTitle = translate('iou.receiptStatusTitle'); amountTitle = translate('iou.receiptStatusTitle'); @@ -376,10 +383,6 @@ function MoneyRequestView({ // Checks applied when creating a new expense // NOTE: receipt field can return multiple violations, so we need to handle it separately const fieldChecks: Partial> = { - amount: { - isError: transactionAmount === 0, - translationPath: canEditAmount ? 'common.error.enterAmount' : 'common.error.missingAmount', - }, merchant: { isError: !isSettled && !isCancelled && isPolicyExpenseChat && isEmptyMerchant, translationPath: canEditMerchant ? 'common.error.enterMerchant' : 'common.error.missingMerchantName', @@ -418,7 +421,6 @@ function MoneyRequestView({ return ''; }, [ - transactionAmount, isSettled, isCancelled, isPolicyExpenseChat, @@ -429,7 +431,6 @@ function MoneyRequestView({ hasViolations, translate, getViolationsForField, - canEditAmount, canEditDate, canEditMerchant, canEdit, diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index a6257dc7b233..b7efdbb7dd4f 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -31,6 +31,7 @@ import { hasMissingSmartscanFields, hasNoticeTypeViolation, hasPendingRTERViolation, + hasReceipt, hasViolation, hasWarningTypeViolation, isAmountMissing, @@ -237,7 +238,7 @@ function getTransactionPreviewTextAndTranslationPaths({ if (hasFieldErrors && RBRMessage === undefined) { const merchantMissing = isMerchantMissing(transaction); - const amountMissing = isAmountMissing(transaction); + const amountMissing = isAmountMissing(transaction) && hasReceipt(transaction); if (amountMissing && merchantMissing) { RBRMessage = {translationPath: 'violations.reviewRequired'}; } else if (amountMissing) { diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 59950398f4a3..72786271f572 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -445,7 +445,8 @@ function areRequiredFieldsEmpty(transaction: OnyxEntry, reportTrans const isFromExpenseReport = parentReport?.type === CONST.REPORT.TYPE.EXPENSE; const isSplitPolicyExpenseChat = !!transaction?.comment?.splits?.some((participant) => allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`]?.isOwnPolicyExpenseChat); const isMerchantRequired = isFromExpenseReport || isSplitPolicyExpenseChat; - return (isMerchantRequired && isMerchantMissing(transaction)) || isAmountMissing(transaction) || isCreatedMissing(transaction); + const isAmountRequired = hasReceipt(transaction); + return (isMerchantRequired && isMerchantMissing(transaction)) || (isAmountMissing(transaction) && isAmountRequired) || isCreatedMissing(transaction); } function getClearedPendingFields(transactionChanges: TransactionChanges) { diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index 1a50af10a360..f49bd6b55f35 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -54,7 +54,7 @@ type MoneyRequestAmountFormProps = Omit !amount.length || parseFloat(amount) < 0.01; +const isAmountInvalid = (amount: string) => !amount.length || parseFloat(amount) < 0; const isTaxAmountInvalid = (currentAmount: string, taxAmount: number, isTaxAmountForm: boolean, currency: string) => isTaxAmountForm && Number.parseFloat(currentAmount) > convertToFrontendAmountAsInteger(Math.abs(taxAmount), currency); diff --git a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx index 9b87b63dbb10..076db633600a 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx @@ -299,7 +299,7 @@ function IOURequestStepDistanceManual({ const submitAndNavigateToNextPage = useCallback(() => { const value = numberFormRef.current?.getNumber() ?? ''; - if (!value.length || parseFloat(value) < 0.01) { + if (!value.length || parseFloat(value) < 0) { setFormError(translate('iou.error.invalidDistance')); return; } diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index a5d74f049d78..6595008d7f9c 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -1206,8 +1206,9 @@ describe('DebugUtils', () => { accountID: 12345, }, [`${ONYXKEYS.COLLECTION.TRANSACTION}1` as const]: { - amount: 0, - modifiedAmount: 0, + amount: 100, + created: '', + modifiedCreated: '', }, }); const {reportAction} = @@ -1269,8 +1270,9 @@ describe('DebugUtils', () => { }; await Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.TRANSACTION}1` as const]: { - amount: 0, - modifiedAmount: 0, + amount: 100, + created: '', + modifiedCreated: '', }, [`${ONYXKEYS.COLLECTION.REPORT}1` as const]: MOCK_IOU_REPORT, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1` as const]: MOCK_REPORT_ACTIONS, @@ -1329,8 +1331,9 @@ describe('DebugUtils', () => { }; await Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.TRANSACTION}1` as const]: { - amount: 0, - modifiedAmount: 0, + amount: 100, + created: '', + modifiedCreated: '', }, [`${ONYXKEYS.COLLECTION.REPORT}1` as const]: MOCK_IOU_REPORT, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1` as const]: MOCK_REPORT_ACTIONS, @@ -1388,8 +1391,9 @@ describe('DebugUtils', () => { }; await Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.TRANSACTION}1` as const]: { - amount: 0, - modifiedAmount: 0, + amount: 100, + created: '', + modifiedCreated: '', }, [`${ONYXKEYS.COLLECTION.REPORT}1` as const]: MOCK_CHAT_REPORT, [`${ONYXKEYS.COLLECTION.REPORT}2` as const]: MOCK_IOU_REPORT, @@ -1453,8 +1457,9 @@ describe('DebugUtils', () => { }; await Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.TRANSACTION}1` as const]: { - amount: 0, - modifiedAmount: 0, + amount: 100, + created: '', + modifiedCreated: '', }, [`${ONYXKEYS.COLLECTION.REPORT}1` as const]: MOCK_CHAT_REPORT, [`${ONYXKEYS.COLLECTION.REPORT}2` as const]: MOCK_IOU_REPORT, @@ -1512,8 +1517,9 @@ describe('DebugUtils', () => { }; await Onyx.multiSet({ [`${ONYXKEYS.COLLECTION.TRANSACTION}1` as const]: { - amount: 0, - modifiedAmount: 0, + amount: 100, + created: '', + modifiedCreated: '', }, [`${ONYXKEYS.COLLECTION.REPORT}1` as const]: MOCK_IOU_REPORT, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1` as const]: MOCK_REPORT_ACTIONS, diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index b903990b70c1..2ea135a8eac5 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -7169,7 +7169,8 @@ describe('ReportUtils', () => { const transaction: Transaction = { ...createRandomTransaction(12345), reportID: parentReport.reportID, - amount: 0, + created: '', + modifiedCreated: '', }; await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, transaction); await waitForBatchedUpdates(); @@ -7194,7 +7195,8 @@ describe('ReportUtils', () => { const transaction: Transaction = { ...createRandomTransaction(12345), reportID: parentReport.reportID, - amount: 0, + created: '', + modifiedCreated: '', }; await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, transaction); await waitForBatchedUpdates(); From a0d0baf20ee64e266c035181a2ad044deef09b68 Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 15 Oct 2025 11:14:30 +0200 Subject: [PATCH 02/65] tests --- tests/unit/TransactionPreviewUtils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/TransactionPreviewUtils.test.ts b/tests/unit/TransactionPreviewUtils.test.ts index 4331f657be60..175d61f99159 100644 --- a/tests/unit/TransactionPreviewUtils.test.ts +++ b/tests/unit/TransactionPreviewUtils.test.ts @@ -107,7 +107,7 @@ describe('TransactionPreviewUtils', () => { it('returns merchant missing and amount missing message when appropriate', () => { const functionArgs = { ...basicProps, - transaction: {...basicProps.transaction, merchant: '', amount: 0}, + transaction: {...basicProps.transaction, merchant: '', amount: 0, receipt: {source: 'test-receipt.jpg', state: CONST.IOU.RECEIPT_STATE.SCAN_COMPLETE}}, originalTransaction: undefined, shouldShowRBR: true, }; From 122318d20cf3205a507711423373c6427817477c Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 15 Oct 2025 11:47:03 +0200 Subject: [PATCH 03/65] performance thingy --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index add7a631bb22..17e97dcbbd2e 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -205,7 +205,7 @@ function MoneyRequestView({ ((isDistanceRequest && hasRoute) || (!!actualAmount && hasReceipt(updatedTransaction ?? transaction)) || !hasReceipt(updatedTransaction ?? transaction)) && actualAmount !== undefined ); - }, [isDistanceRequest, hasRoute, actualAmount, updatedTransaction, transaction]); + }, [isDistanceRequest, hasRoute, actualAmount, updatedTransaction?.receipt, transaction?.receipt]); const formattedTransactionAmount = shouldDisplayTransactionAmount ? convertToDisplayString(actualAmount, actualCurrency) : ''; const formattedPerAttendeeAmount = shouldDisplayTransactionAmount && actualAmount !== undefined ? convertToDisplayString(actualAmount / (transactionAttendees?.length ?? 1), actualCurrency) : ''; From f2d0df6942cf966a8a32db1f4b35494e7ddfd163 Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 15 Oct 2025 12:36:16 +0200 Subject: [PATCH 04/65] change it so zero amount expenses with receipts are also allowed --- src/components/ReportActionItem/MoneyRequestView.tsx | 7 +------ src/libs/TransactionPreviewUtils.ts | 8 +------- src/libs/TransactionUtils/index.ts | 3 +-- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 17e97dcbbd2e..716af2f6461c 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -200,12 +200,7 @@ 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 = useMemo(() => { - return ( - ((isDistanceRequest && hasRoute) || (!!actualAmount && hasReceipt(updatedTransaction ?? transaction)) || !hasReceipt(updatedTransaction ?? transaction)) && - actualAmount !== undefined - ); - }, [isDistanceRequest, hasRoute, actualAmount, updatedTransaction?.receipt, transaction?.receipt]); + const shouldDisplayTransactionAmount = (isDistanceRequest && hasRoute) || !isDistanceRequest; const formattedTransactionAmount = shouldDisplayTransactionAmount ? convertToDisplayString(actualAmount, actualCurrency) : ''; const formattedPerAttendeeAmount = shouldDisplayTransactionAmount && actualAmount !== undefined ? convertToDisplayString(actualAmount / (transactionAttendees?.length ?? 1), actualCurrency) : ''; diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index b7efdbb7dd4f..e35387eb58d1 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -34,7 +34,6 @@ import { hasReceipt, hasViolation, hasWarningTypeViolation, - isAmountMissing, isCardTransaction, isCreatedMissing, isDistanceRequest, @@ -238,12 +237,7 @@ function getTransactionPreviewTextAndTranslationPaths({ if (hasFieldErrors && RBRMessage === undefined) { const merchantMissing = isMerchantMissing(transaction); - const amountMissing = isAmountMissing(transaction) && hasReceipt(transaction); - if (amountMissing && merchantMissing) { - RBRMessage = {translationPath: 'violations.reviewRequired'}; - } else if (amountMissing) { - RBRMessage = {translationPath: 'iou.missingAmount'}; - } else if (merchantMissing) { + if (merchantMissing) { RBRMessage = {translationPath: 'iou.missingMerchant'}; } } diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 72786271f572..78f49eff1041 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -445,8 +445,7 @@ function areRequiredFieldsEmpty(transaction: OnyxEntry, reportTrans const isFromExpenseReport = parentReport?.type === CONST.REPORT.TYPE.EXPENSE; const isSplitPolicyExpenseChat = !!transaction?.comment?.splits?.some((participant) => allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`]?.isOwnPolicyExpenseChat); const isMerchantRequired = isFromExpenseReport || isSplitPolicyExpenseChat; - const isAmountRequired = hasReceipt(transaction); - return (isMerchantRequired && isMerchantMissing(transaction)) || (isAmountMissing(transaction) && isAmountRequired) || isCreatedMissing(transaction); + return (isMerchantRequired && isMerchantMissing(transaction)) || isCreatedMissing(transaction); } function getClearedPendingFields(transactionChanges: TransactionChanges) { From 043d88424694a3baf7c3df182fb38e2ea9176333 Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 15 Oct 2025 12:42:20 +0200 Subject: [PATCH 05/65] remove uneeded --- src/components/ReportActionItem/MoneyRequestView.tsx | 1 - src/libs/TransactionPreviewUtils.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 716af2f6461c..be24be197fa9 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -67,7 +67,6 @@ import { getTagForDisplay, getTaxName, hasMissingSmartscanFields, - hasReceipt, hasReservationList, hasRoute as hasRouteTransactionUtils, isCardTransaction as isCardTransactionTransactionUtils, diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index e35387eb58d1..29913b4c3ed2 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -31,7 +31,6 @@ import { hasMissingSmartscanFields, hasNoticeTypeViolation, hasPendingRTERViolation, - hasReceipt, hasViolation, hasWarningTypeViolation, isCardTransaction, From 66270b593b7cabc91a66d308345e4cbf51bc854b Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 15 Oct 2025 14:23:20 +0200 Subject: [PATCH 06/65] one more test --- tests/unit/TransactionPreviewUtils.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/TransactionPreviewUtils.test.ts b/tests/unit/TransactionPreviewUtils.test.ts index 175d61f99159..aad9ed4c2e64 100644 --- a/tests/unit/TransactionPreviewUtils.test.ts +++ b/tests/unit/TransactionPreviewUtils.test.ts @@ -104,7 +104,7 @@ describe('TransactionPreviewUtils', () => { expect(result.displayAmountText.text).toEqual('$0.00'); }); - it('returns merchant missing and amount missing message when appropriate', () => { + it('returns merchant missing message when appropriate', () => { const functionArgs = { ...basicProps, transaction: {...basicProps.transaction, merchant: '', amount: 0, receipt: {source: 'test-receipt.jpg', state: CONST.IOU.RECEIPT_STATE.SCAN_COMPLETE}}, @@ -112,7 +112,7 @@ describe('TransactionPreviewUtils', () => { shouldShowRBR: true, }; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); - expect(result.RBRMessage.translationPath).toEqual('violations.reviewRequired'); + expect(result.RBRMessage.translationPath).toEqual('iou.missingMerchant'); }); it('should display showCashOrCard in previewHeaderText', () => { From 1f71b90d70071eccdfdf35b04933f964f8bcf82f Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 15 Oct 2025 15:09:39 +0200 Subject: [PATCH 07/65] better test --- tests/unit/TransactionPreviewUtils.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/TransactionPreviewUtils.test.ts b/tests/unit/TransactionPreviewUtils.test.ts index aad9ed4c2e64..47d3e1ac3d6f 100644 --- a/tests/unit/TransactionPreviewUtils.test.ts +++ b/tests/unit/TransactionPreviewUtils.test.ts @@ -104,10 +104,10 @@ describe('TransactionPreviewUtils', () => { expect(result.displayAmountText.text).toEqual('$0.00'); }); - it('returns merchant missing message when appropriate', () => { + it('returns missing field message when appropriate', () => { const functionArgs = { ...basicProps, - transaction: {...basicProps.transaction, merchant: '', amount: 0, receipt: {source: 'test-receipt.jpg', state: CONST.IOU.RECEIPT_STATE.SCAN_COMPLETE}}, + transaction: {...basicProps.transaction, created: '', amount: 100}, originalTransaction: undefined, shouldShowRBR: true, }; From ef79084fda8e111931dc6aacd3d2b9cfe6fe657e Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 16 Oct 2025 12:20:54 +0200 Subject: [PATCH 08/65] Display 0.0 distance correctly --- src/libs/DistanceRequestUtils.ts | 2 +- src/libs/TransactionUtils/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 0d363d599be5..11e51e0d5395 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -168,7 +168,7 @@ function getDistanceForDisplay( translate: LocaleContextProps['translate'], useShortFormUnit?: boolean, ): string { - if (!hasRoute || !unit || !distanceInMeters) { + if (!hasRoute || !unit) { return translate('iou.fieldPending'); } diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 78f49eff1041..de3ea91dfaf0 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1201,7 +1201,7 @@ function hasPendingUI(transaction: OnyxEntry, transactionViolations * Check if the transaction has a defined route */ function hasRoute(transaction: OnyxEntry, isDistanceRequestType?: boolean): boolean { - return !!transaction?.routes?.route0?.geometry?.coordinates || (!!isDistanceRequestType && !!transaction?.comment?.customUnit?.quantity); + return !!transaction?.routes?.route0?.geometry?.coordinates || (!!isDistanceRequestType && transaction?.comment?.customUnit?.name === 'Distance'); } function waypointHasValidAddress(waypoint: RecentWaypoint | Waypoint): boolean { From f2480a5ae96fde12cc5fffa69c589a5e4353b73d Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 16 Oct 2025 12:45:47 +0200 Subject: [PATCH 09/65] allow zero as modified amount --- src/libs/TransactionUtils/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index de3ea91dfaf0..206bb123d523 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -429,6 +429,10 @@ function isAmountMissing(transaction: OnyxEntry) { return transaction?.amount === 0 && (!transaction.modifiedAmount || transaction.modifiedAmount === 0); } +function hasValidModifiedAmount(transaction: OnyxEntry) { + return transaction?.modifiedAmount !== undefined && transaction?.modifiedAmount !== null && transaction?.modifiedAmount !== ''; +} + function isPartial(transaction: OnyxEntry): boolean { return isPartialMerchant(getMerchant(transaction)) && isAmountMissing(transaction); } @@ -693,7 +697,7 @@ function getDescription(transaction: OnyxInputOrEntry): string { function getAmount(transaction: OnyxInputOrEntry, isFromExpenseReport = false, isFromTrackedExpense = false, allowNegative = false, disableOppositeConversion = false): number { // IOU requests cannot have negative values, but they can be stored as negative values, let's return absolute value if (!isFromExpenseReport && !isFromTrackedExpense && !allowNegative) { - const amount = transaction?.modifiedAmount ?? 0; + const amount = hasValidModifiedAmount(transaction) ? transaction?.modifiedAmount : 0; if (amount) { return Math.abs(amount); } @@ -707,7 +711,7 @@ function getAmount(transaction: OnyxInputOrEntry, isFromExpenseRepo // Expense report case: // The amounts are stored using an opposite sign and negative values can be set, // we need to return an opposite sign than is saved in the transaction object - let amount = transaction?.modifiedAmount ?? 0; + let amount = hasValidModifiedAmount(transaction) ? transaction?.modifiedAmount : 0; if (amount) { return -amount; } From 982281bc1ab3f7d6231fe8018e3619bbdeef87eb Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 16 Oct 2025 12:51:19 +0200 Subject: [PATCH 10/65] better display fix --- src/libs/TransactionUtils/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 206bb123d523..1939b76c4285 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -697,8 +697,8 @@ function getDescription(transaction: OnyxInputOrEntry): string { function getAmount(transaction: OnyxInputOrEntry, isFromExpenseReport = false, isFromTrackedExpense = false, allowNegative = false, disableOppositeConversion = false): number { // IOU requests cannot have negative values, but they can be stored as negative values, let's return absolute value if (!isFromExpenseReport && !isFromTrackedExpense && !allowNegative) { - const amount = hasValidModifiedAmount(transaction) ? transaction?.modifiedAmount : 0; - if (amount) { + const amount = transaction?.modifiedAmount ?? 0; + if (hasValidModifiedAmount(transaction)) { return Math.abs(amount); } return Math.abs(transaction?.amount ?? 0); @@ -711,8 +711,8 @@ function getAmount(transaction: OnyxInputOrEntry, isFromExpenseRepo // Expense report case: // The amounts are stored using an opposite sign and negative values can be set, // we need to return an opposite sign than is saved in the transaction object - let amount = hasValidModifiedAmount(transaction) ? transaction?.modifiedAmount : 0; - if (amount) { + let amount = transaction?.modifiedAmount ?? 0; + if (hasValidModifiedAmount(transaction)) { return -amount; } From b6449cab6e533799a7ab0ff30df1e1ca7c744d58 Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 22 Oct 2025 15:40:11 +0200 Subject: [PATCH 11/65] prevent zero amount for pay actions --- src/pages/iou/MoneyRequestAmountForm.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index f49bd6b55f35..cae9f71c3baa 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -54,7 +54,7 @@ type MoneyRequestAmountFormProps = Omit !amount.length || parseFloat(amount) < 0; +const isAmountInvalid = (amount: string, iouType?: ValueOf) => !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && iouType === CONST.IOU.TYPE.PAY); const isTaxAmountInvalid = (currentAmount: string, taxAmount: number, isTaxAmountForm: boolean, currency: string) => isTaxAmountForm && Number.parseFloat(currentAmount) > convertToFrontendAmountAsInteger(Math.abs(taxAmount), currency); @@ -143,7 +143,7 @@ function MoneyRequestAmountForm({ // Skip the check for tax amount form as 0 is a valid input const currentAmount = moneyRequestAmountInputRef.current?.getNumber() ?? ''; - if (!currentAmount.length || (!isTaxAmountForm && isAmountInvalid(currentAmount))) { + if (!currentAmount.length || (!isTaxAmountForm && isAmountInvalid(currentAmount, iouType))) { setFormError(translate('iou.error.invalidAmount')); return; } From edc372fee1f4dfba6068dc8eb34037e409a3bd0d Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 22 Oct 2025 17:03:51 +0200 Subject: [PATCH 12/65] allow zero --- src/libs/MergeTransactionUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/MergeTransactionUtils.ts b/src/libs/MergeTransactionUtils.ts index 76ad0e4a68a6..26f42864f19f 100644 --- a/src/libs/MergeTransactionUtils.ts +++ b/src/libs/MergeTransactionUtils.ts @@ -190,7 +190,7 @@ function getMergeableDataAndConflictFields(targetTransaction: OnyxEntry Date: Wed, 22 Oct 2025 17:04:10 +0200 Subject: [PATCH 13/65] keep existing behavior --- src/libs/MergeTransactionUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/MergeTransactionUtils.ts b/src/libs/MergeTransactionUtils.ts index 26f42864f19f..76ad0e4a68a6 100644 --- a/src/libs/MergeTransactionUtils.ts +++ b/src/libs/MergeTransactionUtils.ts @@ -190,7 +190,7 @@ function getMergeableDataAndConflictFields(targetTransaction: OnyxEntry Date: Thu, 23 Oct 2025 13:40:19 +0200 Subject: [PATCH 14/65] prevent zero amount for invoices and show merchant field when needed --- src/pages/iou/MoneyRequestAmountForm.tsx | 2 +- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index cae9f71c3baa..8a6c9cdc7dc6 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -54,7 +54,7 @@ type MoneyRequestAmountFormProps = Omit) => !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && iouType === CONST.IOU.TYPE.PAY); +const isAmountInvalid = (amount: string, iouType?: ValueOf) => !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && (iouType === CONST.IOU.TYPE.PAY || iouType === CONST.IOU.TYPE.INVOICE)); const isTaxAmountInvalid = (currentAmount: string, taxAmount: number, isTaxAmountForm: boolean, currency: string) => isTaxAmountForm && Number.parseFloat(currentAmount) > convertToFrontendAmountAsInteger(Math.abs(taxAmount), currency); diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 064b5953f93f..7a740a682ac9 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -1159,7 +1159,7 @@ function IOURequestStepConfirmation({ const showReceiptEmptyState = shouldShowReceiptEmptyState(iouType, action, policy, isPerDiemRequest, isManualDistanceRequest); const shouldShowSmartScanFields = - !!transaction?.receipt?.isTestDriveReceipt || (isMovingTransactionFromTrackExpense ? transaction?.amount !== 0 : requestType !== CONST.IOU.REQUEST_TYPE.SCAN); + !!transaction?.receipt?.isTestDriveReceipt || isMovingTransactionFromTrackExpense || requestType !== CONST.IOU.REQUEST_TYPE.SCAN; return ( Date: Thu, 23 Oct 2025 16:22:17 +0200 Subject: [PATCH 15/65] prevent zero on splits, because you can't split zero --- src/pages/iou/MoneyRequestAmountForm.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index 8a6c9cdc7dc6..4177daf34d4a 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -54,7 +54,8 @@ type MoneyRequestAmountFormProps = Omit) => !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && (iouType === CONST.IOU.TYPE.PAY || iouType === CONST.IOU.TYPE.INVOICE)); +const nonZeroExpenses: Array> = [CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.INVOICE, CONST.IOU.TYPE.SPLIT]; +const isAmountInvalid = (amount: string, iouType: ValueOf) => !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && nonZeroExpenses.includes(iouType)); const isTaxAmountInvalid = (currentAmount: string, taxAmount: number, isTaxAmountForm: boolean, currency: string) => isTaxAmountForm && Number.parseFloat(currentAmount) > convertToFrontendAmountAsInteger(Math.abs(taxAmount), currency); From 635a6b5882a25f336b5009f1507bdcaba0cba7a9 Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 28 Oct 2025 15:37:55 +0100 Subject: [PATCH 16/65] pass isDistance --- src/libs/actions/IOU.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index d1bfe45de55c..00878ed23e69 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6517,6 +6517,7 @@ function trackExpense(params: CreateTrackExpenseParams) { waypoints: sanitizedWaypoints, customUnitRateID, description: parsedComment, + isDistance: isMapDistanceRequest(transaction) || isManualDistanceRequestTransactionUtils(transaction), }; if (actionableWhisperReportActionIDParam) { parameters.actionableWhisperReportActionID = actionableWhisperReportActionIDParam; From 699a313f3d31573d9afa9e55c30d78d1b0a7b43b Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 28 Oct 2025 16:09:40 +0100 Subject: [PATCH 17/65] prettier --- src/pages/iou/MoneyRequestAmountForm.tsx | 3 ++- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index 4177daf34d4a..c1ba9cf41806 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -55,7 +55,8 @@ type MoneyRequestAmountFormProps = Omit> = [CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.INVOICE, CONST.IOU.TYPE.SPLIT]; -const isAmountInvalid = (amount: string, iouType: ValueOf) => !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && nonZeroExpenses.includes(iouType)); +const isAmountInvalid = (amount: string, iouType: ValueOf) => + !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && nonZeroExpenses.includes(iouType)); const isTaxAmountInvalid = (currentAmount: string, taxAmount: number, isTaxAmountForm: boolean, currency: string) => isTaxAmountForm && Number.parseFloat(currentAmount) > convertToFrontendAmountAsInteger(Math.abs(taxAmount), currency); diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 7a740a682ac9..41428d4e678d 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -1158,8 +1158,7 @@ function IOURequestStepConfirmation({ const showReceiptEmptyState = shouldShowReceiptEmptyState(iouType, action, policy, isPerDiemRequest, isManualDistanceRequest); - const shouldShowSmartScanFields = - !!transaction?.receipt?.isTestDriveReceipt || isMovingTransactionFromTrackExpense || requestType !== CONST.IOU.REQUEST_TYPE.SCAN; + const shouldShowSmartScanFields = !!transaction?.receipt?.isTestDriveReceipt || isMovingTransactionFromTrackExpense || requestType !== CONST.IOU.REQUEST_TYPE.SCAN; return ( Date: Tue, 28 Oct 2025 16:15:46 +0100 Subject: [PATCH 18/65] typescript validation --- src/libs/TransactionUtils/index.ts | 2 +- src/types/onyx/Transaction.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 5daedf641aa0..64e0db3cdb22 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -702,7 +702,7 @@ function getDescription(transaction: OnyxInputOrEntry): string { function getAmount(transaction: OnyxInputOrEntry, isFromExpenseReport = false, isFromTrackedExpense = false, allowNegative = false, disableOppositeConversion = false): number { // IOU requests cannot have negative values, but they can be stored as negative values, let's return absolute value if (!isFromExpenseReport && !isFromTrackedExpense && !allowNegative) { - const amount = transaction?.modifiedAmount ?? 0; + const amount = transaction?.modifiedAmount as number ?? 0; if (hasValidModifiedAmount(transaction)) { return Math.abs(amount); } diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index bbe5c4b9fcb0..6a25ac9fe8bb 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -446,7 +446,7 @@ type Transaction = OnyxCommon.OnyxValueWithOfflineFeedback< merchant: string; /** The edited transaction amount */ - modifiedAmount?: number; + modifiedAmount?: number | string; /** The edited attendees list */ modifiedAttendees?: Attendee[]; From 243bbfceb45283f5ecb3f9ffee3bae63dc7ca975 Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 28 Oct 2025 16:21:37 +0100 Subject: [PATCH 19/65] more of that --- src/libs/API/parameters/TrackExpenseParams.ts | 1 + src/libs/TransactionUtils/index.ts | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/API/parameters/TrackExpenseParams.ts b/src/libs/API/parameters/TrackExpenseParams.ts index 7cb8fb86a9ec..7da368e0eeb9 100644 --- a/src/libs/API/parameters/TrackExpenseParams.ts +++ b/src/libs/API/parameters/TrackExpenseParams.ts @@ -32,6 +32,7 @@ type TrackExpenseParams = { customUnitRateID?: string; description?: string; distance?: number; + isDistance?: boolean; }; export default TrackExpenseParams; diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 64e0db3cdb22..9999ecc69e1f 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -430,7 +430,10 @@ function isAmountMissing(transaction: OnyxEntry) { return transaction?.amount === 0 && (!transaction.modifiedAmount || transaction.modifiedAmount === 0); } -function hasValidModifiedAmount(transaction: OnyxEntry) { +function hasValidModifiedAmount(transaction: OnyxEntry | null): boolean { + if (!transaction) { + return false; + } return transaction?.modifiedAmount !== undefined && transaction?.modifiedAmount !== null && transaction?.modifiedAmount !== ''; } @@ -716,7 +719,7 @@ function getAmount(transaction: OnyxInputOrEntry, isFromExpenseRepo // Expense report case: // The amounts are stored using an opposite sign and negative values can be set, // we need to return an opposite sign than is saved in the transaction object - let amount = transaction?.modifiedAmount ?? 0; + let amount = transaction?.modifiedAmount as number ?? 0; if (hasValidModifiedAmount(transaction)) { return -amount; } From f20fc68f4ea722a42f336c94eee59756a7b13727 Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 28 Oct 2025 16:43:36 +0100 Subject: [PATCH 20/65] and more --- .../ReportActionItem/MoneyRequestReportPreview/index.tsx | 2 +- src/libs/ReportUtils.ts | 4 ++-- src/libs/SearchUIUtils.ts | 2 +- src/libs/TransactionUtils/index.ts | 4 ++-- src/libs/actions/IOU.ts | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index d9cceea897a7..f4a80b467390 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -96,7 +96,7 @@ function MoneyRequestReportPreview({ } // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - return transactions.some((transaction) => (transaction?.modifiedAmount || transaction?.amount) < 0); + return transactions.some((transaction) => (Number(transaction?.modifiedAmount) || transaction?.amount) < 0); }, [transactions, action.childType, iouReport]); const openReportFromPreview = useCallback(() => { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f38019098b0f..53937617a865 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5179,7 +5179,7 @@ function getModifiedExpenseOriginalMessage( 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(updatedTransaction?.modifiedAmount ?? 0); + originalMessage.amount = Math.abs(Number(updatedTransaction?.modifiedAmount ?? 0)); originalMessage.currency = updatedTransaction?.modifiedCurrency ?? CONST.CURRENCY.USD; originalMessage.merchant = updatedTransaction?.modifiedMerchant; } @@ -11618,7 +11618,7 @@ function hasExportError(reportActions: OnyxEntry | ReportAction[] function doesReportContainRequestsFromMultipleUsers(iouReport: OnyxEntry): boolean { const transactions = getReportTransactions(iouReport?.reportID); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - return isIOUReport(iouReport) && transactions.some((transaction) => (transaction?.modifiedAmount || transaction?.amount) < 0); + return isIOUReport(iouReport) && transactions.some((transaction) => (Number(transaction?.modifiedAmount) || transaction?.amount) < 0); } /** diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 564c373c5c60..e4c90e9683c0 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -798,7 +798,7 @@ function isAmountTooLong(amount: number, maxLength = 8): boolean { function isTransactionAmountTooLong(transactionItem: TransactionListItemType | SearchTransaction | OnyxTypes.Transaction) { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const amount = Math.abs(transactionItem.modifiedAmount || transactionItem.amount); + const amount = Math.abs(Number(transactionItem.modifiedAmount) || transactionItem.amount); return isAmountTooLong(amount); } diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 9999ecc69e1f..e0e4d9f499fb 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -705,7 +705,7 @@ function getDescription(transaction: OnyxInputOrEntry): string { function getAmount(transaction: OnyxInputOrEntry, isFromExpenseReport = false, isFromTrackedExpense = false, allowNegative = false, disableOppositeConversion = false): number { // IOU requests cannot have negative values, but they can be stored as negative values, let's return absolute value if (!isFromExpenseReport && !isFromTrackedExpense && !allowNegative) { - const amount = transaction?.modifiedAmount as number ?? 0; + const amount = Number(transaction?.modifiedAmount) ?? 0; if (hasValidModifiedAmount(transaction)) { return Math.abs(amount); } @@ -719,7 +719,7 @@ function getAmount(transaction: OnyxInputOrEntry, isFromExpenseRepo // Expense report case: // The amounts are stored using an opposite sign and negative values can be set, // we need to return an opposite sign than is saved in the transaction object - let amount = transaction?.modifiedAmount as number ?? 0; + let amount = Number(transaction?.modifiedAmount) ?? 0; if (hasValidModifiedAmount(transaction)) { return -amount; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 00878ed23e69..90e5495022ba 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -7695,7 +7695,7 @@ function completeSplitBill( ]; const splitParticipants: Split[] = updatedTransaction?.comment?.splits ?? []; - const amount = updatedTransaction?.modifiedAmount; + const amount = Number(updatedTransaction?.modifiedAmount); const currency = updatedTransaction?.modifiedCurrency; // Exclude the current user when calculating the split amount, `calculateAmount` takes it into account From 2bb910da2d676b8abf082a767d56693233a0cf6c Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 28 Oct 2025 17:05:27 +0100 Subject: [PATCH 21/65] one more --- src/components/ReportActionItem/TransactionPreview/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/TransactionPreview/index.tsx b/src/components/ReportActionItem/TransactionPreview/index.tsx index 5685d336c89c..f208962b84c7 100644 --- a/src/components/ReportActionItem/TransactionPreview/index.tsx +++ b/src/components/ReportActionItem/TransactionPreview/index.tsx @@ -92,7 +92,7 @@ function TransactionPreview(props: TransactionPreviewProps) { // See description of `transactionRawAmount` prop for more context // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const transactionRawAmount = (transaction?.modifiedAmount || transaction?.amount) ?? 0; + const transactionRawAmount = (Number(transaction?.modifiedAmount) || transaction?.amount) ?? 0; const shouldDisableOnPress = isBillSplit && isEmptyObject(transaction); const isTransactionMadeWithCard = isManagedCardTransaction(transaction); From 574b00e1894f473a3056838823b5c7f548551579 Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 28 Oct 2025 17:29:29 +0100 Subject: [PATCH 22/65] lint --- src/libs/Violations/ViolationsUtils.ts | 2 +- src/libs/actions/Policy/Policy.ts | 2 +- src/libs/actions/Report.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 80999381f056..4f63a6e8ad65 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -322,7 +322,7 @@ const ViolationsUtils = { const isTaxInPolicy = Object.keys(policy.taxRates?.taxes ?? {}).some((key) => key === updatedTransaction.taxCode); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const amount = updatedTransaction.modifiedAmount || updatedTransaction.amount; + const amount = Number(updatedTransaction.modifiedAmount) || updatedTransaction.amount; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const currency = updatedTransaction.modifiedCurrency || updatedTransaction.currency; const canCalculateAmountViolations = policy.outputCurrency === currency; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 0b51f01bfece..e4dba508d81b 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -3680,7 +3680,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF transactionsOptimisticData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = { ...transaction, amount: -transaction.amount, - modifiedAmount: transaction.modifiedAmount ? -transaction.modifiedAmount : 0, + modifiedAmount: transaction.modifiedAmount ? -Number(transaction.modifiedAmount) : 0, }; transactionFailureData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = transaction; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 1b9277f9a8ab..a1c72b4d7bf9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -5512,7 +5512,7 @@ function convertIOUReportToExpenseReport(iouReport: Report, policy: Policy, poli transactionsOptimisticData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = { ...transaction, amount: -transaction.amount, - modifiedAmount: transaction.modifiedAmount ? -transaction.modifiedAmount : 0, + modifiedAmount: transaction.modifiedAmount ? -Number(transaction.modifiedAmount) : 0, }; transactionFailureData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = transaction; From 13a84d307cb80ab519bd107cf2250b6911673436 Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 29 Oct 2025 12:48:57 +0100 Subject: [PATCH 23/65] update tests --- tests/unit/Search/SearchUIUtilsTest.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 77fb3fd2514c..de726e6b62de 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -324,7 +324,7 @@ const searchResults: OnyxTypes.SearchResults = { description: '', hasViolation: false, merchant: 'Expense', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: 'Expense', @@ -370,7 +370,7 @@ const searchResults: OnyxTypes.SearchResults = { description: '', hasViolation: true, merchant: 'Expense', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: 'Expense', @@ -415,7 +415,7 @@ const searchResults: OnyxTypes.SearchResults = { isFromOneTransactionReport: false, managerID: approverAccountID, merchant: '(none)', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: '', @@ -461,7 +461,7 @@ const searchResults: OnyxTypes.SearchResults = { isFromOneTransactionReport: false, managerID: approverAccountID, merchant: '(none)', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: '', @@ -690,7 +690,7 @@ const transactionsListItems = [ keyForList: '1', managerID: 18439984, merchant: 'Expense', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: 'Expense', @@ -758,7 +758,7 @@ const transactionsListItems = [ keyForList: '2', managerID: 18439984, merchant: 'Expense', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: 'Expense', @@ -819,7 +819,7 @@ const transactionsListItems = [ isFromOneTransactionReport: false, managerID: 1111111, merchant: '(none)', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: '', @@ -887,7 +887,7 @@ const transactionsListItems = [ isFromOneTransactionReport: false, managerID: 1111111, merchant: '(none)', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: '', @@ -1006,7 +1006,7 @@ const transactionReportGroupListItems = [ keyForList: '1', managerID: 18439984, merchant: 'Expense', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: 'Expense', @@ -1118,7 +1118,7 @@ const transactionReportGroupListItems = [ keyForList: '2', managerID: 18439984, merchant: 'Expense', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: 'Expense', From b9e7c2d254ddae75307421a49fe501e634a08335 Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 29 Oct 2025 13:06:17 +0100 Subject: [PATCH 24/65] typing --- src/types/onyx/SearchResults.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/SearchResults.ts b/src/types/onyx/SearchResults.ts index d8ceb87a1db0..5176cecdc3b0 100644 --- a/src/types/onyx/SearchResults.ts +++ b/src/types/onyx/SearchResults.ts @@ -322,7 +322,7 @@ type SearchTransaction = { canUnhold: boolean; /** The edited transaction amount */ - modifiedAmount: number; + modifiedAmount: number | string; /** The transaction currency */ currency: string; From 7b3c5bd93a7f0c5a594984b81046a93820bc1d78 Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 29 Oct 2025 13:11:37 +0100 Subject: [PATCH 25/65] and more typing --- src/components/Search/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index d3ca32e5a765..4e068797bd9b 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -110,7 +110,7 @@ function mapTransactionItemToSelectedEntry( action: item.action, reportID: item.reportID, policyID: item.policyID, - amount: item.modifiedAmount ?? item.amount, + amount: Number(item.modifiedAmount ?? item.amount), convertedAmount: item.convertedAmount, isFromOneTransactionReport: item.isFromOneTransactionReport, convertedCurrency: item.convertedCurrency, @@ -192,7 +192,7 @@ function prepareTransactionsList( action: item.action, reportID: item.reportID, policyID: item.policyID, - amount: Math.abs(item.modifiedAmount || item.amount), + amount: Math.abs(Number(item.modifiedAmount || item.amount)), convertedAmount: item.convertedAmount, convertedCurrency: item.convertedCurrency, currency: item.currency, @@ -453,7 +453,7 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS canDelete: transaction.canDelete, reportID: transaction.reportID, policyID: transaction.policyID, - amount: transaction.modifiedAmount ?? transaction.amount, + amount: Number(transaction.modifiedAmount ?? transaction.amount), convertedAmount: transaction.convertedAmount, convertedCurrency: transaction.convertedCurrency, currency: transaction.currency, @@ -486,7 +486,7 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS canDelete: transaction.canDelete, reportID: transaction.reportID, policyID: transaction.policyID, - amount: transaction.modifiedAmount ?? transaction.amount, + amount: Number(transaction.modifiedAmount ?? transaction.amount), convertedAmount: transaction.convertedAmount, convertedCurrency: transaction.convertedCurrency, currency: transaction.currency, From 6ec45d39bebeb238c9c1242a1fca7a528f839f56 Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 29 Oct 2025 16:30:57 +0100 Subject: [PATCH 26/65] lint --- src/libs/ReportUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 47830101d933..d366e4bd3220 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1004,6 +1004,7 @@ Onyx.connect({ return acc; } + // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { handlePreexistingReport(report); }); From cdaff0c15c3d0388e2f5cd7bb2d5d938316f9a0c Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 29 Oct 2025 20:07:09 +0100 Subject: [PATCH 27/65] better check for scan requests --- src/libs/TransactionUtils/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 9c618a1f1602..907255629dfd 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -155,7 +155,7 @@ Onyx.connect({ }, }); -function hasDistanceCustomUnit(transaction: OnyxEntry): boolean { +function hasDistanceCustomUnit(transaction: OnyxEntry | Partial): boolean { const type = transaction?.comment?.type; const customUnitName = transaction?.comment?.customUnit?.name; return type === CONST.TRANSACTION.TYPE.CUSTOM_UNIT && customUnitName === CONST.CUSTOM_UNITS.NAME_DISTANCE; @@ -211,6 +211,11 @@ function isScanRequest(transaction: OnyxEntry | Partial Date: Thu, 30 Oct 2025 13:17:03 +0100 Subject: [PATCH 28/65] test updating --- tests/actions/IOUTest.ts | 6 +++--- tests/unit/Search/handleActionButtonPressTest.ts | 4 ++-- tests/utils/collections/transaction.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 43d6f82ed3a8..ae5c504cc5f1 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -6269,7 +6269,7 @@ describe('actions/IOU', () => { ...createRandomTransaction(0), reportID, amount: 0, - modifiedAmount: 0, + modifiedAmount: '', receipt: { source: 'test', state: CONST.IOU.RECEIPT_STATE.SCAN_FAILED, @@ -6281,7 +6281,7 @@ describe('actions/IOU', () => { ...createRandomTransaction(1), reportID, amount: 0, - modifiedAmount: 0, + modifiedAmount: '', receipt: { source: 'test', state: CONST.IOU.RECEIPT_STATE.SCAN_FAILED, @@ -6331,7 +6331,7 @@ describe('actions/IOU', () => { ...createRandomTransaction(1), reportID, amount: 0, - modifiedAmount: 0, + modifiedAmount: '', receipt: { source: 'test', state: CONST.IOU.RECEIPT_STATE.SCAN_FAILED, diff --git a/tests/unit/Search/handleActionButtonPressTest.ts b/tests/unit/Search/handleActionButtonPressTest.ts index 19a8f46fb9b2..46be477b04f6 100644 --- a/tests/unit/Search/handleActionButtonPressTest.ts +++ b/tests/unit/Search/handleActionButtonPressTest.ts @@ -97,7 +97,7 @@ const mockReportItemWithHold = { isFromOneTransactionReport: false, managerID: 1206, merchant: 'Qatar', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: '', @@ -185,7 +185,7 @@ const mockReportItemWithHold = { isFromOneTransactionReport: false, managerID: 1206, merchant: 'Forbes', - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMerchant: '', diff --git a/tests/utils/collections/transaction.ts b/tests/utils/collections/transaction.ts index 3f9fdf7ca1e3..c4c3e69c0834 100644 --- a/tests/utils/collections/transaction.ts +++ b/tests/utils/collections/transaction.ts @@ -42,6 +42,6 @@ export default function createRandomTransaction(index: number): Transaction { receipt: {}, reimbursable: randBoolean(), hasEReceipt: randBoolean(), - modifiedAmount: 0, + modifiedAmount: '', }; } From 195207108917f335daf6305a2bf2b774a5fd7cfb Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 30 Oct 2025 13:30:40 +0100 Subject: [PATCH 29/65] and more tests --- tests/actions/IOUTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index ae5c504cc5f1..7127f699dd12 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -6806,7 +6806,7 @@ describe('actions/IOU', () => { }, }); }); - expect(updatedTransaction?.modifiedAmount).toBe(0); + expect(updatedTransaction?.modifiedAmount).toBe(''); }); }); From 6038dbbf73a745bbcdbbb99910c519fb149f397d Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 30 Oct 2025 13:59:11 +0100 Subject: [PATCH 30/65] more updates --- __mocks__/reportData/transactions.ts | 4 ++-- src/stories/objects/Transaction.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/__mocks__/reportData/transactions.ts b/__mocks__/reportData/transactions.ts index 4dc0cfdd1902..dfc621ed90a7 100644 --- a/__mocks__/reportData/transactions.ts +++ b/__mocks__/reportData/transactions.ts @@ -30,7 +30,7 @@ const transactionR14932: Transaction = { reimbursable: true, hasEReceipt: true, cardID: 0, - modifiedAmount: 0, + modifiedAmount: '', originalAmount: 0, comment: {}, bank: '', @@ -59,7 +59,7 @@ const transactionR98765: Transaction = { hasEReceipt: true, managedCard: false, billable: false, - modifiedAmount: 0, + modifiedAmount: '', cardID: 0, originalAmount: 0, comment: {}, diff --git a/src/stories/objects/Transaction.ts b/src/stories/objects/Transaction.ts index ffc3d71a4f5f..22551325bbd7 100644 --- a/src/stories/objects/Transaction.ts +++ b/src/stories/objects/Transaction.ts @@ -21,7 +21,7 @@ const transaction: Transaction & {mcc: string; modifiedMCC: string} = { managedCard: false, mcc: '', merchant: "Mario's", - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMCC: '', From 3c523b96babcf611b1e0277740aaf30c71230256 Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 30 Oct 2025 14:04:59 +0100 Subject: [PATCH 31/65] typo --- src/stories/objects/Transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stories/objects/Transaction.ts b/src/stories/objects/Transaction.ts index 22551325bbd7..ffc3d71a4f5f 100644 --- a/src/stories/objects/Transaction.ts +++ b/src/stories/objects/Transaction.ts @@ -21,7 +21,7 @@ const transaction: Transaction & {mcc: string; modifiedMCC: string} = { managedCard: false, mcc: '', merchant: "Mario's", - modifiedAmount: '', + modifiedAmount: 0, modifiedCreated: '', modifiedCurrency: '', modifiedMCC: '', From 006b45e05453df2ca1bbd5919068ffc41bcd9539 Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 5 Nov 2025 16:37:44 +0100 Subject: [PATCH 32/65] improve some modifiedAmount behavior --- src/components/Search/index.tsx | 4 ++-- src/libs/TransactionUtils/index.ts | 1 + src/libs/actions/Policy/Policy.ts | 3 ++- src/libs/actions/Report.ts | 4 ++-- src/stories/objects/Transaction.ts | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 96e81e090ca6..5863c62f8c96 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -55,7 +55,7 @@ import { shouldShowEmptyState, shouldShowYear as shouldShowYearUtil, } from '@libs/SearchUIUtils'; -import {isOnHold, isTransactionPendingDelete, shouldShowViolation} from '@libs/TransactionUtils'; +import {isOnHold, isTransactionPendingDelete, shouldShowViolation, hasValidModifiedAmount} from '@libs/TransactionUtils'; import Navigation, {navigationRef} from '@navigation/Navigation'; import type {SearchFullscreenNavigatorParamList} from '@navigation/types'; import EmptySearchView from '@pages/Search/EmptySearchView'; @@ -472,7 +472,7 @@ function Search({ canDelete: transactionItem.canDelete, reportID: transactionItem.reportID, policyID: transactionItem.policyID, - amount: Number(transactionItem.modifiedAmount ?? transactionItem.amount), + amount: hasValidModifiedAmount(transactionItem) ? Number(transactionItem.modifiedAmount) : transactionItem.amount, convertedAmount: transactionItem.convertedAmount, convertedCurrency: transactionItem.convertedCurrency, currency: transactionItem.currency, diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index b4ab3bb7a299..0e55a090c1ad 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -2107,6 +2107,7 @@ export { areRequiredFieldsEmpty, hasMissingSmartscanFields, hasPendingRTERViolation, + hasValidModifiedAmount, allHavePendingRTERViolation, hasPendingUI, getWaypointIndex, diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 4ef20e0085da..0f753fcf1e76 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -132,6 +132,7 @@ import type {NotificationPreference} from '@src/types/onyx/Report'; import type {OnyxData} from '@src/types/onyx/Request'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {buildOptimisticMccGroup, buildOptimisticPolicyCategories, buildOptimisticPolicyWithExistingCategories} from './Category'; +import {hasValidModifiedAmount} from "@libs/TransactionUtils"; type ReportCreationData = Record< string, @@ -3690,7 +3691,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF transactionsOptimisticData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = { ...transaction, amount: -transaction.amount, - modifiedAmount: transaction.modifiedAmount ? -Number(transaction.modifiedAmount) : 0, + modifiedAmount: hasValidModifiedAmount(transaction) ? -Number(transaction.modifiedAmount) : '', }; transactionFailureData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = transaction; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7bb9c82cf3c4..d24d8238607f 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -170,7 +170,7 @@ import {getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; import playSound, {SOUNDS} from '@libs/Sound'; -import {isOnHold} from '@libs/TransactionUtils'; +import {hasValidModifiedAmount, isOnHold} from '@libs/TransactionUtils'; import addTrailingForwardSlash from '@libs/UrlUtils'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; @@ -5530,7 +5530,7 @@ function convertIOUReportToExpenseReport(iouReport: Report, policy: Policy, poli transactionsOptimisticData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = { ...transaction, amount: -transaction.amount, - modifiedAmount: transaction.modifiedAmount ? -Number(transaction.modifiedAmount) : 0, + modifiedAmount: hasValidModifiedAmount(transaction) ? -Number(transaction.modifiedAmount) : '', }; transactionFailureData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = transaction; diff --git a/src/stories/objects/Transaction.ts b/src/stories/objects/Transaction.ts index ffc3d71a4f5f..22551325bbd7 100644 --- a/src/stories/objects/Transaction.ts +++ b/src/stories/objects/Transaction.ts @@ -21,7 +21,7 @@ const transaction: Transaction & {mcc: string; modifiedMCC: string} = { managedCard: false, mcc: '', merchant: "Mario's", - modifiedAmount: 0, + modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', modifiedMCC: '', From 870fe8946bf33124bc25cc9f0bb913eed81537be Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 13:48:30 +0100 Subject: [PATCH 33/65] prettier --- src/components/Search/index.tsx | 6 +++--- src/libs/actions/Policy/Policy.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 40086b251503..cfc8fc425f60 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -55,7 +55,7 @@ import { shouldShowEmptyState, shouldShowYear as shouldShowYearUtil, } from '@libs/SearchUIUtils'; -import {isOnHold, isTransactionPendingDelete, shouldShowViolation, hasValidModifiedAmount} from '@libs/TransactionUtils'; +import {hasValidModifiedAmount, isOnHold, isTransactionPendingDelete, shouldShowViolation} from '@libs/TransactionUtils'; import Navigation, {navigationRef} from '@navigation/Navigation'; import type {SearchFullscreenNavigatorParamList} from '@navigation/types'; import EmptySearchView from '@pages/Search/EmptySearchView'; @@ -114,7 +114,7 @@ function mapTransactionItemToSelectedEntry(item: TransactionListItemType, outsta convertedCurrency: item.convertedCurrency, reportID: item.reportID, policyID: item.report?.policyID, - amount: hasValidModifiedAmount(transactionItem) ? Number(transactionItem.modifiedAmount) : transactionItem.amount, + amount: hasValidModifiedAmount(item) ? Number(item.modifiedAmount) : item.amount, convertedAmount: item.convertedAmount, currency: item.currency, isFromOneTransactionReport: item.isFromOneTransactionReport, @@ -193,7 +193,7 @@ function prepareTransactionsList(item: TransactionListItemType, selectedTransact action: item.action, reportID: item.reportID, policyID: item.policyID, - amount: Math.abs(hasValidModifiedAmount(item) ? Number(item.modifiedAmount) : item.amount,), + amount: Math.abs(hasValidModifiedAmount(item) ? Number(item.modifiedAmount) : item.amount), convertedAmount: item.convertedAmount, convertedCurrency: item.convertedCurrency, currency: item.currency, diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index e864174897e6..bc0d410b86c0 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -95,6 +95,7 @@ import { navigateToReceiptPartnersPage, } from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import {hasValidModifiedAmount} from '@libs/TransactionUtils'; import type {PolicySelector} from '@pages/home/sidebar/FloatingActionButtonAndPopover'; import type {Feature} from '@pages/OnboardingInterestedFeatures/types'; import * as PaymentMethods from '@userActions/PaymentMethods'; @@ -132,7 +133,6 @@ import type {NotificationPreference} from '@src/types/onyx/Report'; import type {OnyxData} from '@src/types/onyx/Request'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {buildOptimisticMccGroup, buildOptimisticPolicyCategories, buildOptimisticPolicyWithExistingCategories} from './Category'; -import {hasValidModifiedAmount} from "@libs/TransactionUtils"; type ReportCreationData = Record< string, From 2cc4db13ec15dce767969bdf95bcee7957f3cc8d Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 13:57:00 +0100 Subject: [PATCH 34/65] deprecated searchReports --- tests/unit/ReportUtilsTest.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index c6eaa5754d62..f5080b391852 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -8390,7 +8390,7 @@ describe('ReportUtils', () => { describe('getReportOrDraftReport', () => { const mockReportIDIndex = 1; const mockReportID = mockReportIDIndex.toString(); - const mockSearchReport: SearchReport = { + const mockSearchReport: Report = { ...createRandomReport(mockReportIDIndex, undefined), reportName: 'Search Report', type: CONST.REPORT.TYPE.CHAT, @@ -8419,27 +8419,27 @@ describe('ReportUtils', () => { }); test('returns onyx report when search report is not found but onyx report exists', async () => { - const searchReports: SearchReport[] = []; + const searchReports: Report[] = []; await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`, mockOnyxReport); const result = getReportOrDraftReport(mockReportID, searchReports); expect(result).toEqual(mockOnyxReport); }); test('returns draft report when neither search nor onyx report exists but draft exists', async () => { - const searchReports: SearchReport[] = []; + const searchReports: Report[] = []; await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${mockReportID}`, mockDraftReport); const result = getReportOrDraftReport(mockReportID, searchReports); expect(result).toEqual(mockDraftReport); }); test('returns fallback report when no other reports exist', () => { - const searchReports: SearchReport[] = []; + const searchReports: Report[] = []; const result = getReportOrDraftReport('unknownReportID', searchReports, mockFallbackReport); expect(result).toEqual(mockFallbackReport); }); test('returns undefined when no reports exist and no fallback provided', () => { - const searchReports: SearchReport[] = []; + const searchReports: Report[] = []; const result = getReportOrDraftReport(mockReportID, searchReports); expect(result).toBeUndefined(); }); @@ -8470,7 +8470,7 @@ describe('ReportUtils', () => { }); test('prioritizes onyx report over draft report when both exist', async () => { - const searchReports: SearchReport[] = []; + const searchReports: Report[] = []; await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`, mockOnyxReport); await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${mockReportID}`, mockDraftReport); const result = getReportOrDraftReport(mockReportID, searchReports); @@ -8479,7 +8479,7 @@ describe('ReportUtils', () => { }); test('prioritizes draft report over fallback when both exist', async () => { - const searchReports: SearchReport[] = []; + const searchReports: Report[] = []; await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${mockReportID}`, mockDraftReport); const result = getReportOrDraftReport(mockReportID, searchReports, mockFallbackReport); expect(result).toEqual(mockDraftReport); From fa7373625aad5370569550bf2ad90deb35f78b1d Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 14:14:14 +0100 Subject: [PATCH 35/65] one more --- tests/unit/ReportUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index f5080b391852..a5e3e30e6c11 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -120,7 +120,7 @@ import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; import type {JoinWorkspaceResolution} from '@src/types/onyx/OriginalMessage'; import type {ACHAccount} from '@src/types/onyx/Policy'; import type {Participant, Participants} from '@src/types/onyx/Report'; -import type {SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; +import type {SearchTransaction} from '@src/types/onyx/SearchResults'; import {toCollectionDataSet} from '@src/types/utils/CollectionDataSet'; import {chatReportR14932 as mockedChatReport} from '../../__mocks__/reportData/reports'; import * as NumberUtils from '../../src/libs/NumberUtils'; From 76411b9f0aaae328854e22ade77da88576d963c0 Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 14:37:56 +0100 Subject: [PATCH 36/65] try report --- src/libs/ReportUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 658d57453f9d..a616a327e868 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1203,8 +1203,7 @@ function getChatType(report: OnyxInputOrEntry | Participant): ValueOf | SearchReport { +function getReportOrDraftReport(reportID: string | undefined, searchReports?: Report[], fallbackReport?: Report): OnyxEntry { const searchReport = searchReports?.find((report) => report.reportID === reportID); const onyxReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; return searchReport ?? onyxReport ?? allReportsDraft?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`] ?? fallbackReport; From d67c01d2d8cb81ea6a02b1f1392da07bbed6b515 Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 14:41:58 +0100 Subject: [PATCH 37/65] more lint --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a616a327e868..66a8d7e20c2d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10576,7 +10576,7 @@ function shouldCreateNewMoneyRequestReport( } function getTripIDFromTransactionParentReportID(transactionParentReportID: string | undefined): string | undefined { - return (getReportOrDraftReport(transactionParentReportID) as OnyxEntry)?.tripData?.tripID; + return getReportOrDraftReport(transactionParentReportID)?.tripData?.tripID; } /** From 123e2e8fa9a96d34a5e6e6c16085c6c1babbed9c Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 15:49:47 +0100 Subject: [PATCH 38/65] Default to default merchant for manual expenses --- src/libs/TransactionUtils/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 01ad11f1d934..0b2f6f2b71fd 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -371,6 +371,8 @@ function buildOptimisticTransaction(params: BuildOptimisticTransactionParams): T lodashSet(commentJSON, 'customUnit', customUnit); } + const isManualTransaction = !isPerDiemTransaction && !isMapDistanceTransaction && !isManualDistanceTransaction && !splitExpenses && !receipt?.source; + return { ...(!isEmptyObject(pendingFields) ? {pendingFields} : {}), transactionID, @@ -378,7 +380,7 @@ function buildOptimisticTransaction(params: BuildOptimisticTransactionParams): T currency, reportID, comment: commentJSON, - merchant: merchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, + merchant: merchant || isManualTransaction ? CONST.TRANSACTION.DEFAULT_MERCHANT : CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, created: created || DateUtils.getDBTime(), pendingAction, receipt: receipt?.source From e22b006d6bb482a0aa50d45414144b2b530377da Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 15:49:57 +0100 Subject: [PATCH 39/65] also for the draft --- src/libs/actions/IOU.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e2a8ae297043..4fd075558b9a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1092,6 +1092,8 @@ function initMoneyRequest({ } } + const defaultMerchant = newIouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL ? CONST.TRANSACTION.DEFAULT_MERCHANT : CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; + const newTransaction = { amount: 0, comment, @@ -1102,7 +1104,7 @@ function initMoneyRequest({ reportID, transactionID: newTransactionID, isFromGlobalCreate, - merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, + merchant: defaultMerchant, }; // Store the transaction in Onyx and mark it as not saved so it can be cleaned up later From 56b985bcf2b8dcb7ba5284bd4c3cafb5fb3c0b62 Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 15:56:25 +0100 Subject: [PATCH 40/65] Several default checks --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- src/components/TransactionItemRow/index.tsx | 2 +- src/libs/SearchUIUtils.ts | 4 ++-- src/pages/iou/request/step/IOURequestStepMerchant.tsx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 00c2d90158bf..a6d8e2274609 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -203,7 +203,7 @@ function MoneyRequestView({ originalCurrency: transactionOriginalCurrency, postedDate: transactionPostedDate, } = useMemo>(() => getTransactionDetails(transaction, undefined, undefined, allowNegativeAmount) ?? {}, [allowNegativeAmount, transaction]); - const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; + const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transactionMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT; const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); const isManualDistanceRequest = isManualDistanceRequestTransactionUtils(transaction); const isMapDistanceRequest = isDistanceRequest && !isManualDistanceRequest; diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index ce8d1b05a3a1..f090f966de1f 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -126,7 +126,7 @@ function getMerchantName(transactionItem: TransactionWithOptionalSearchFields, t } const merchantName = StringUtils.getFirstLine(merchant); - return merchantName !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT ? merchantName : ''; + return (merchantName !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && merchantName !== CONST.TRANSACTION.DEFAULT_MERCHANT) ? merchantName : ''; } function TransactionItemRow({ diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 1eb58eb64326..a1eef4a314e7 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -637,7 +637,7 @@ function getTransactionItemCommonFormattedProperties( const formattedTotal = getTransactionAmount(transactionItem, isExpenseReport); const date = transactionItem?.modifiedCreated ? transactionItem.modifiedCreated : transactionItem?.created; const merchant = getTransactionMerchant(transactionItem, policy); - const formattedMerchant = merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT ? '' : merchant; + const formattedMerchant = (merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || merchant === CONST.TRANSACTION.DEFAULT_MERCHANT) ? '' : merchant; return { formattedFrom, @@ -695,7 +695,7 @@ function getShouldShowMerchant(data: OnyxTypes.SearchResults['data']): boolean { if (isTransactionEntry(key)) { const item = data[key]; const merchant = item.modifiedMerchant ? item.modifiedMerchant : (item.merchant ?? ''); - return merchant !== '' && merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; + return merchant !== '' && merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && merchant !== CONST.TRANSACTION.DEFAULT_MERCHANT; } return false; }); diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index 5b478dc50dad..049a829656a7 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -52,7 +52,7 @@ function IOURequestStepMerchant({ // In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value const isEditingSplitBill = iouType === CONST.IOU.TYPE.SPLIT && isEditing; const merchant = getTransactionDetails(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction)?.merchant; - const isEmptyMerchant = merchant === '' || merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; + const isEmptyMerchant = merchant === '' || merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || merchant === CONST.TRANSACTION.DEFAULT_MERCHANT; const initialMerchant = isEmptyMerchant ? '' : merchant; const merchantRef = useRef(initialMerchant); const isSavedRef = useRef(false); From 59a9745e9996003835d2c7d9b4fe66438c3ce8f7 Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 16:04:21 +0100 Subject: [PATCH 41/65] And more default checks --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- src/libs/SearchUIUtils.ts | 2 +- src/libs/TransactionUtils/index.ts | 4 ++-- src/pages/AddUnreportedExpense.tsx | 2 +- src/pages/iou/request/step/IOURequestStepMerchant.tsx | 2 +- tests/unit/AddUnreportedExpenseSearchTest.ts | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index a6d8e2274609..0e26c10a3517 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -323,7 +323,7 @@ function MoneyRequestView({ } return getDescription(updatedTransaction ?? null); }, [updatedTransaction]); - const isEmptyUpdatedMerchant = updatedTransaction?.modifiedMerchant === '' || updatedTransaction?.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; + const isEmptyUpdatedMerchant = updatedTransaction?.modifiedMerchant === '' || updatedTransaction?.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || updatedTransaction?.modifiedMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT; const updatedMerchantTitle = isEmptyUpdatedMerchant ? '' : (updatedTransaction?.modifiedMerchant ?? merchantTitle); const saveBillable = useCallback( diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index a1eef4a314e7..f96f841ae4b5 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -2305,7 +2305,7 @@ function getColumnsToShow( const updateColumns = (transaction: OnyxTypes.Transaction | SearchTransaction) => { const merchant = transaction.modifiedMerchant ? transaction.modifiedMerchant : (transaction.merchant ?? ''); - if ((merchant !== '' && merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT) || isScanning(transaction)) { + if ((merchant !== '' && merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && merchant !== CONST.TRANSACTION.DEFAULT_MERCHANT) || isScanning(transaction)) { columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.MERCHANT] = true; } diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 0b2f6f2b71fd..73eff7c53805 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -421,9 +421,9 @@ function isDemoTransaction(transaction: OnyxInputOrEntry): boolean function isMerchantMissing(transaction: OnyxEntry) { if (transaction?.modifiedMerchant && transaction.modifiedMerchant !== '') { - return transaction.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; + return (transaction.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction.modifiedMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT); } - const isMerchantEmpty = transaction?.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction?.merchant === ''; + const isMerchantEmpty = transaction?.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction?.merchant === CONST.TRANSACTION.DEFAULT_MERCHANT || transaction?.merchant === ''; return isMerchantEmpty; } diff --git a/src/pages/AddUnreportedExpense.tsx b/src/pages/AddUnreportedExpense.tsx index 2853619fb2a3..0f911974fa19 100644 --- a/src/pages/AddUnreportedExpense.tsx +++ b/src/pages/AddUnreportedExpense.tsx @@ -130,7 +130,7 @@ function AddUnreportedExpense({route}: AddUnreportedExpensePageType) { const searchableFields: string[] = []; const merchant = getMerchant(transaction); - if (merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT) { + if (merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && merchant !== CONST.TRANSACTION.DEFAULT_MERCHANT) { searchableFields.push(merchant); } diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index 049a829656a7..cd20606fda08 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -70,7 +70,7 @@ function IOURequestStepMerchant({ if (isMerchantRequired && !value.moneyRequestMerchant) { errors.moneyRequestMerchant = translate('common.error.fieldRequired'); - } else if (isMerchantRequired && value.moneyRequestMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT) { + } else if (isMerchantRequired && (value.moneyRequestMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || value.moneyRequestMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT)) { errors.moneyRequestMerchant = translate('iou.error.invalidMerchant'); } else if (!isValid) { errors.moneyRequestMerchant = translate('common.error.characterLimitExceedCounter', { diff --git a/tests/unit/AddUnreportedExpenseSearchTest.ts b/tests/unit/AddUnreportedExpenseSearchTest.ts index 6c7e90dce970..5eccc644c0ef 100644 --- a/tests/unit/AddUnreportedExpenseSearchTest.ts +++ b/tests/unit/AddUnreportedExpenseSearchTest.ts @@ -107,7 +107,7 @@ describe('AddUnreportedExpense Search Functionality', () => { // Add merchant to searchable fields const merchant = getMerchant(transaction); - if (merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT) { + if (merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && merchant !== CONST.TRANSACTION.DEFAULT_MERCHANT) { searchableFields.push(merchant); } From 768770f7de84b50844b7c63106e7af46a3522b2f Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 16:12:08 +0100 Subject: [PATCH 42/65] and some more --- src/components/ReportActionItem/MoneyRequestView.tsx | 8 ++++++-- src/components/TransactionItemRow/index.tsx | 2 +- src/libs/SearchUIUtils.ts | 2 +- src/libs/TransactionUtils/index.ts | 5 +++-- src/pages/iou/request/step/IOURequestStepMerchant.tsx | 5 ++++- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 0e26c10a3517..94bea63a9b57 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -203,7 +203,8 @@ function MoneyRequestView({ originalCurrency: transactionOriginalCurrency, postedDate: transactionPostedDate, } = useMemo>(() => getTransactionDetails(transaction, undefined, undefined, allowNegativeAmount) ?? {}, [allowNegativeAmount, transaction]); - const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transactionMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT; + const isEmptyMerchant = + transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transactionMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT; const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); const isManualDistanceRequest = isManualDistanceRequestTransactionUtils(transaction); const isMapDistanceRequest = isDistanceRequest && !isManualDistanceRequest; @@ -323,7 +324,10 @@ function MoneyRequestView({ } return getDescription(updatedTransaction ?? null); }, [updatedTransaction]); - 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 || + updatedTransaction?.modifiedMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT; const updatedMerchantTitle = isEmptyUpdatedMerchant ? '' : (updatedTransaction?.modifiedMerchant ?? merchantTitle); const saveBillable = useCallback( diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index f090f966de1f..9c801d72dd19 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -126,7 +126,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 !== CONST.TRANSACTION.DEFAULT_MERCHANT ? merchantName : ''; } function TransactionItemRow({ diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index f96f841ae4b5..e4a16c6a7023 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -637,7 +637,7 @@ function getTransactionItemCommonFormattedProperties( const formattedTotal = getTransactionAmount(transactionItem, isExpenseReport); const date = transactionItem?.modifiedCreated ? transactionItem.modifiedCreated : transactionItem?.created; const merchant = getTransactionMerchant(transactionItem, policy); - const formattedMerchant = (merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || merchant === CONST.TRANSACTION.DEFAULT_MERCHANT) ? '' : merchant; + const formattedMerchant = merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || merchant === CONST.TRANSACTION.DEFAULT_MERCHANT ? '' : merchant; return { formattedFrom, diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 73eff7c53805..6a4d3eb1bece 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -421,9 +421,10 @@ function isDemoTransaction(transaction: OnyxInputOrEntry): boolean function isMerchantMissing(transaction: OnyxEntry) { if (transaction?.modifiedMerchant && transaction.modifiedMerchant !== '') { - return (transaction.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction.modifiedMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT); + return transaction.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction.modifiedMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT; } - const isMerchantEmpty = transaction?.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction?.merchant === CONST.TRANSACTION.DEFAULT_MERCHANT || transaction?.merchant === ''; + const isMerchantEmpty = + transaction?.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction?.merchant === CONST.TRANSACTION.DEFAULT_MERCHANT || transaction?.merchant === ''; return isMerchantEmpty; } diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index cd20606fda08..f1141b8a60b4 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -70,7 +70,10 @@ function IOURequestStepMerchant({ if (isMerchantRequired && !value.moneyRequestMerchant) { errors.moneyRequestMerchant = translate('common.error.fieldRequired'); - } else if (isMerchantRequired && (value.moneyRequestMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || value.moneyRequestMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT)) { + } else if ( + isMerchantRequired && + (value.moneyRequestMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || value.moneyRequestMerchant === CONST.TRANSACTION.DEFAULT_MERCHANT) + ) { errors.moneyRequestMerchant = translate('iou.error.invalidMerchant'); } else if (!isValid) { errors.moneyRequestMerchant = translate('common.error.characterLimitExceedCounter', { From 11f05af5ddea97939674c1874d1bcc35492e8841 Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 16:47:21 +0100 Subject: [PATCH 43/65] Update tests --- tests/unit/TransactionUtilsTest.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index 0eb538d2ea26..f8fe0fbab741 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -405,6 +405,7 @@ describe('TransactionUtils', () => { receipt: { state: CONST.IOU.RECEIPT_STATE.SCAN_READY, }, + merchant: '(none)' }); expect(TransactionUtils.shouldShowRTERViolationMessage([transaction])).toBe(true); }); @@ -521,7 +522,7 @@ describe('TransactionUtils', () => { it('should return (none) if transaction has no merchant', () => { const transaction = generateTransaction(); const merchant = TransactionUtils.getMerchant(transaction); - expect(merchant).toBe('(none)'); + expect(merchant).toBe('Expense'); }); it('should return modified merchant if transaction has modified merchant', () => { From 4c1313ef7da4d07ae5be3d1afa9a578c56868c8e Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 16:51:53 +0100 Subject: [PATCH 44/65] prettier --- tests/unit/TransactionUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index f8fe0fbab741..a42c1f567a26 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -405,7 +405,7 @@ describe('TransactionUtils', () => { receipt: { state: CONST.IOU.RECEIPT_STATE.SCAN_READY, }, - merchant: '(none)' + merchant: '(none)', }); expect(TransactionUtils.shouldShowRTERViolationMessage([transaction])).toBe(true); }); From 438b0aef0ce2a9f7f41aa4051f48876cb4ed1826 Mon Sep 17 00:00:00 2001 From: alberto Date: Thu, 6 Nov 2025 17:37:40 +0100 Subject: [PATCH 45/65] more tests --- tests/unit/TransactionPreviewUtils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/TransactionPreviewUtils.test.ts b/tests/unit/TransactionPreviewUtils.test.ts index 47d3e1ac3d6f..c0e8859f0beb 100644 --- a/tests/unit/TransactionPreviewUtils.test.ts +++ b/tests/unit/TransactionPreviewUtils.test.ts @@ -138,7 +138,7 @@ describe('TransactionPreviewUtils', () => { }); it('displays description when receipt is being scanned', () => { - const functionArgs = {...basicProps, transaction: {...basicProps.transaction, receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING}}, originalTransaction: undefined}; + const functionArgs = {...basicProps, transaction: {...basicProps.transaction, receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING}}, originalTransaction: undefined, merchant: 'Expense'}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); expect(result.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'common.receipt'}])); }); From 5aaaa0550d09ba14c9b32f80690c20ca08ff4d2c Mon Sep 17 00:00:00 2001 From: alberto Date: Fri, 7 Nov 2025 10:49:04 +0100 Subject: [PATCH 46/65] redo some stuff the merge got rid of --- src/components/Search/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 7f3713b5d7af..62b0c3648199 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -193,7 +193,7 @@ function prepareTransactionsList(item: TransactionListItemType, selectedTransact action: item.action, reportID: item.reportID, policyID: item.policyID, - amount: Math.abs(item.modifiedAmount || item.amount), + amount: hasValidModifiedAmount(item) ? Number(item.modifiedAmount) : item.amount, convertedAmount: item.convertedAmount, convertedCurrency: item.convertedCurrency, currency: item.currency, From 561b1488b963ac66fc4db4bf0a766fdae13ff832 Mon Sep 17 00:00:00 2001 From: alberto Date: Fri, 7 Nov 2025 11:20:33 +0100 Subject: [PATCH 47/65] more lint --- src/pages/iou/MoneyRequestAmountForm.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index c1ba9cf41806..6dc68f4a1eee 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -54,9 +54,9 @@ type MoneyRequestAmountFormProps = Omit> = [CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.INVOICE, CONST.IOU.TYPE.SPLIT]; +const nonZeroExpenses: Set> = new Set([CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.INVOICE, CONST.IOU.TYPE.SPLIT]); const isAmountInvalid = (amount: string, iouType: ValueOf) => - !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && nonZeroExpenses.includes(iouType)); + !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && nonZeroExpenses.has(iouType)); const isTaxAmountInvalid = (currentAmount: string, taxAmount: number, isTaxAmountForm: boolean, currency: string) => isTaxAmountForm && Number.parseFloat(currentAmount) > convertToFrontendAmountAsInteger(Math.abs(taxAmount), currency); From ac23be84e945261e1a53c45f1ee1be04e861bf29 Mon Sep 17 00:00:00 2001 From: alberto Date: Fri, 7 Nov 2025 13:02:19 +0100 Subject: [PATCH 48/65] prettier again --- src/pages/iou/MoneyRequestAmountForm.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index 6dc68f4a1eee..95d688e3e413 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -54,9 +54,8 @@ type MoneyRequestAmountFormProps = Omit> = new Set([CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.INVOICE, CONST.IOU.TYPE.SPLIT]); -const isAmountInvalid = (amount: string, iouType: ValueOf) => - !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && nonZeroExpenses.has(iouType)); +const nonZeroExpenses = new Set>([CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.INVOICE, CONST.IOU.TYPE.SPLIT]); +const isAmountInvalid = (amount: string, iouType: ValueOf) => !amount.length || parseFloat(amount) < 0 || (parseFloat(amount) < 0.01 && nonZeroExpenses.has(iouType)); const isTaxAmountInvalid = (currentAmount: string, taxAmount: number, isTaxAmountForm: boolean, currency: string) => isTaxAmountForm && Number.parseFloat(currentAmount) > convertToFrontendAmountAsInteger(Math.abs(taxAmount), currency); From 11d5558775e241f260690078635329c33ba5ca40 Mon Sep 17 00:00:00 2001 From: alberto Date: Fri, 7 Nov 2025 13:21:14 +0100 Subject: [PATCH 49/65] unrelated lint because typescript hates me --- src/pages/iou/SplitExpensePage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/SplitExpensePage.tsx b/src/pages/iou/SplitExpensePage.tsx index 2ced8c784670..cb6582cb3f68 100644 --- a/src/pages/iou/SplitExpensePage.tsx +++ b/src/pages/iou/SplitExpensePage.tsx @@ -239,7 +239,7 @@ function SplitExpensePage({route}: SplitExpensePageProps) { const items: SplitListItemType[] = (draftTransaction?.comment?.splitExpenses ?? []).map((item): SplitListItemType => { const previewHeaderText: TranslationPathOrText[] = [showCashOrCard]; const currentTransaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${item?.transactionID}`]; - const currentReport = getReportOrDraftReport(currentTransaction?.reportID) as Report; + const currentReport = getReportOrDraftReport(currentTransaction?.reportID)!; const isApproved = isReportApproved({report: currentReport}); const isSettled = isSettledReportUtils(currentReport?.reportID); const isCancelled = currentReport && currentReport?.isCancelledIOU; From fa896e520c6c115f397db566e2dbdecc707b3a76 Mon Sep 17 00:00:00 2001 From: alberto Date: Fri, 7 Nov 2025 13:31:41 +0100 Subject: [PATCH 50/65] but not as much as I hate it --- src/pages/iou/SplitExpensePage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/SplitExpensePage.tsx b/src/pages/iou/SplitExpensePage.tsx index cb6582cb3f68..f497dc81ac04 100644 --- a/src/pages/iou/SplitExpensePage.tsx +++ b/src/pages/iou/SplitExpensePage.tsx @@ -239,7 +239,8 @@ function SplitExpensePage({route}: SplitExpensePageProps) { const items: SplitListItemType[] = (draftTransaction?.comment?.splitExpenses ?? []).map((item): SplitListItemType => { const previewHeaderText: TranslationPathOrText[] = [showCashOrCard]; const currentTransaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${item?.transactionID}`]; - const currentReport = getReportOrDraftReport(currentTransaction?.reportID)!; + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + const currentReport = getReportOrDraftReport(currentTransaction?.reportID) as Report; const isApproved = isReportApproved({report: currentReport}); const isSettled = isSettledReportUtils(currentReport?.reportID); const isCancelled = currentReport && currentReport?.isCancelledIOU; From 7cb252fea27fffcf1cc6b85668bc9600b712c2bd Mon Sep 17 00:00:00 2001 From: alberto Date: Mon, 10 Nov 2025 11:32:57 +0100 Subject: [PATCH 51/65] more tests --- src/libs/TransactionUtils/index.ts | 2 +- tests/unit/Search/SearchUIUtilsTest.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 6a4d3eb1bece..e707543a96e7 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -380,7 +380,7 @@ function buildOptimisticTransaction(params: BuildOptimisticTransactionParams): T currency, reportID, comment: commentJSON, - merchant: merchant || isManualTransaction ? CONST.TRANSACTION.DEFAULT_MERCHANT : CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, + merchant: merchant || (isManualTransaction ? CONST.TRANSACTION.DEFAULT_MERCHANT : CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT), created: created || DateUtils.getDBTime(), pendingAction, receipt: receipt?.source diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 560d4a8a5ac4..b5bc3982052a 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -812,11 +812,11 @@ const transactionsListItems = [ hasEReceipt: false, isFromOneTransactionReport: true, keyForList: '1', - merchant: 'Expense', + merchant: 'Expenses', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expense', + modifiedMerchant: 'Expenses', parentTransactionID: '', pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, reportID: '123456789', From 856aeac9a05932dad92bb762db4c4fa8fa0386a4 Mon Sep 17 00:00:00 2001 From: alberto Date: Mon, 10 Nov 2025 12:49:55 +0100 Subject: [PATCH 52/65] and more --- tests/unit/TransactionPreviewUtils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/TransactionPreviewUtils.test.ts b/tests/unit/TransactionPreviewUtils.test.ts index a23e488cdf3e..09463b769eb8 100644 --- a/tests/unit/TransactionPreviewUtils.test.ts +++ b/tests/unit/TransactionPreviewUtils.test.ts @@ -140,7 +140,7 @@ describe('TransactionPreviewUtils', () => { it('displays description when receipt is being scanned', () => { const functionArgs = { ...basicProps, - transaction: {...basicProps.transaction, receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING}}, + transaction: {...basicProps.transaction, merchant: '(none)', receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING}}, originalTransaction: undefined, merchant: 'Expense', }; From c4ac032aae91d890937160c0dcb1defd280cd213 Mon Sep 17 00:00:00 2001 From: alberto Date: Mon, 10 Nov 2025 14:42:07 +0100 Subject: [PATCH 53/65] even more --- tests/actions/IOUTest.ts | 2 +- tests/unit/Search/SearchUIUtilsTest.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index afcb6e9453a6..1eeea0e2bef5 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1093,7 +1093,7 @@ describe('actions/IOU', () => { attendees: [], currency: CONST.CURRENCY.USD, created: '', - merchant: '', + merchant: '(none)', comment, }, shouldGenerateTransactionThreadReport: true, diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index b5bc3982052a..eab1f5d8621f 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -800,7 +800,7 @@ const transactionsListItems = [ currency: 'USD', date: '2024-12-21', formattedFrom: 'Admin', - formattedMerchant: 'Expense', + formattedMerchant: '', formattedTo: '', formattedTotal: 5000, from: { @@ -868,7 +868,7 @@ const transactionsListItems = [ currency: 'USD', date: '2024-12-21', formattedFrom: 'Admin', - formattedMerchant: 'Expense', + formattedMerchant: '', formattedTo: 'Admin', formattedTotal: 5000, from: { @@ -1114,7 +1114,7 @@ const transactionReportGroupListItems = [ date: '2024-12-21', description: '', formattedFrom: 'Admin', - formattedMerchant: 'Expense', + formattedMerchant: '', formattedTo: '', formattedTotal: 5000, from: { @@ -1219,7 +1219,7 @@ const transactionReportGroupListItems = [ date: '2024-12-21', description: '', formattedFrom: 'Admin', - formattedMerchant: 'Expense', + formattedMerchant: '', formattedTo: 'Admin', formattedTotal: 5000, from: { From 2147e031ea2f44ce7dc15809e7c602131550a7c3 Mon Sep 17 00:00:00 2001 From: alberto Date: Mon, 10 Nov 2025 15:10:58 +0100 Subject: [PATCH 54/65] hopefully last one --- tests/unit/TransactionPreviewUtils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/TransactionPreviewUtils.test.ts b/tests/unit/TransactionPreviewUtils.test.ts index 09463b769eb8..608bd8a0bcf9 100644 --- a/tests/unit/TransactionPreviewUtils.test.ts +++ b/tests/unit/TransactionPreviewUtils.test.ts @@ -158,7 +158,7 @@ describe('TransactionPreviewUtils', () => { const functionArgs = { ...basicProps, transactionDetails: {amount: 300, currency: 'EUR'}, - transaction: {...basicProps.transaction, receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING}}, + transaction: {...basicProps.transaction, merchant: '(none)', receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING}}, originalTransaction: undefined, }; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); From d93099608ac5a248471e65f41ca5aa6597779e0c Mon Sep 17 00:00:00 2001 From: alberto Date: Mon, 10 Nov 2025 16:01:30 +0100 Subject: [PATCH 55/65] some cleanup --- src/libs/actions/Policy/Member.ts | 1 + tests/actions/IOUTest.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index ea9630754ef1..12d8b8cbafec 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -915,6 +915,7 @@ function buildAddMembersToWorkspaceOnyxData( }; successMembersState[email] = {pendingAction: null}; failureMembersState[email] = { + pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.people.error.genericAdd'), }; }); diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 1eeea0e2bef5..68adc15ffe99 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1400,7 +1400,7 @@ describe('actions/IOU', () => { expect(newTransaction?.reportID).toBe(iouReportID); expect(newTransaction?.amount).toBe(amount); expect(newTransaction?.comment?.comment).toBe(comment); - expect(newTransaction?.merchant).toBe(CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT); + expect(newTransaction?.merchant).toBe(CONST.TRANSACTION.DEFAULT_MERCHANT); expect(newTransaction?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); // The transactionID on the iou action should match the one from the transactions collection From 8b7ff253e824bc7fcd050ca172601b4aab417585 Mon Sep 17 00:00:00 2001 From: alberto Date: Mon, 10 Nov 2025 19:44:58 +0100 Subject: [PATCH 56/65] fix expenses --- tests/unit/Search/SearchUIUtilsTest.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index eab1f5d8621f..44e73293a148 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -385,11 +385,11 @@ const searchResults: OnyxTypes.SearchResults = { isFromOneTransactionReport: true, description: '', hasViolation: false, - merchant: 'Expense', + merchant: 'Expenses', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expense', + modifiedMerchant: 'Expenses', parentTransactionID: '', pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, reportID, @@ -428,11 +428,11 @@ const searchResults: OnyxTypes.SearchResults = { isFromOneTransactionReport: true, description: '', hasViolation: true, - merchant: 'Expense', + merchant: 'Expenses', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expense', + modifiedMerchant: 'Expenses', parentTransactionID: '', reportID: reportID2, reportType: 'expense', @@ -880,11 +880,11 @@ const transactionsListItems = [ hasEReceipt: false, isFromOneTransactionReport: true, keyForList: '2', - merchant: 'Expense', + merchant: 'Expenses', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expense', + modifiedMerchant: 'Expenses', parentTransactionID: '', reportID: '11111', reportType: 'expense', @@ -1127,11 +1127,11 @@ const transactionReportGroupListItems = [ hasViolation: false, isFromOneTransactionReport: true, keyForList: '1', - merchant: 'Expense', + merchant: 'Expenses', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expense', + modifiedMerchant: 'Expenses', parentTransactionID: '', pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, reportID: '123456789', @@ -1238,11 +1238,11 @@ const transactionReportGroupListItems = [ ], isFromOneTransactionReport: true, keyForList: '2', - merchant: 'Expense', + merchant: 'Expenses', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expense', + modifiedMerchant: 'Expenses', parentTransactionID: '', reportID: '11111', reportType: 'expense', From 316335e6064296e0050e8e844466bc65b065c8ca Mon Sep 17 00:00:00 2001 From: alberto Date: Mon, 10 Nov 2025 20:40:14 +0100 Subject: [PATCH 57/65] this never ends --- tests/actions/IOUTest.ts | 6 +++--- tests/unit/Search/SearchUIUtilsTest.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 68adc15ffe99..1e1a5e57e5ba 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1191,7 +1191,7 @@ describe('actions/IOU', () => { // The comment should be correct expect(transaction?.comment?.comment).toBe(comment); - expect(transaction?.merchant).toBe(CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT); + expect(transaction?.merchant).toBe(CONST.TRANSACTION.DEFAULT_MERCHANT); // It should be pending expect(transaction?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); @@ -1570,7 +1570,7 @@ describe('actions/IOU', () => { expect(transaction?.reportID).toBe(iouReportID); expect(transaction?.amount).toBe(amount); expect(transaction?.comment?.comment).toBe(comment); - expect(transaction?.merchant).toBe(CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT); + expect(transaction?.merchant).toBe(CONST.TRANSACTION.DEFAULT_MERCHANT); expect(transaction?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); // The transactionID on the iou action should match the one from the transactions collection @@ -6623,7 +6623,7 @@ describe('actions/IOU', () => { reportID: fakeReport.reportID, transactionID: CONST.IOU.OPTIMISTIC_TRANSACTION_ID, isFromGlobalCreate: true, - merchant: '(none)', + merchant: 'Expense', }; const currentDate = '2025-04-01'; diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 44e73293a148..43fad7292c35 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -385,11 +385,11 @@ const searchResults: OnyxTypes.SearchResults = { isFromOneTransactionReport: true, description: '', hasViolation: false, - merchant: 'Expenses', + merchant: 'Expense', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expenses', + modifiedMerchant: 'Expense', parentTransactionID: '', pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, reportID, @@ -428,11 +428,11 @@ const searchResults: OnyxTypes.SearchResults = { isFromOneTransactionReport: true, description: '', hasViolation: true, - merchant: 'Expenses', + merchant: 'Expense', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expenses', + modifiedMerchant: 'Expense', parentTransactionID: '', reportID: reportID2, reportType: 'expense', From 8de4e873a45738c5c74f51c3eccd95c1a4fd7cc9 Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 11 Nov 2025 01:57:40 +0100 Subject: [PATCH 58/65] a bit more --- tests/actions/IOUTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 1e1a5e57e5ba..d8931290c81e 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1191,7 +1191,7 @@ describe('actions/IOU', () => { // The comment should be correct expect(transaction?.comment?.comment).toBe(comment); - expect(transaction?.merchant).toBe(CONST.TRANSACTION.DEFAULT_MERCHANT); + expect(transaction?.merchant).toBe(CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT); // It should be pending expect(transaction?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); From 653dcfbfa0a0ebf9b4f3c80e61e5560ab6b4048f Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 11 Nov 2025 10:47:05 +0100 Subject: [PATCH 59/65] we keep going --- tests/unit/Search/SearchUIUtilsTest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 09d4b75ae889..119a01498e98 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -382,11 +382,11 @@ const searchResults: OnyxTypes.SearchResults = { currency: 'USD', hasEReceipt: false, isFromOneTransactionReport: true, - merchant: 'Expense', + merchant: 'Expenses', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expense', + modifiedMerchant: 'Expenses', parentTransactionID: '', pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, reportID, @@ -422,11 +422,11 @@ const searchResults: OnyxTypes.SearchResults = { currency: 'USD', hasEReceipt: false, isFromOneTransactionReport: true, - merchant: 'Expense', + merchant: 'Expenses', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expense', + modifiedMerchant: 'Expenses', parentTransactionID: '', reportID: reportID2, reportType: 'expense', From d8a247cd172035be4f000604f2f98f34ca2bbe6a Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 11 Nov 2025 11:57:36 +0100 Subject: [PATCH 60/65] Could this be it? --- tests/actions/IOUTest.ts | 1 + tests/unit/Search/SearchUIUtilsTest.ts | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 4cee46c828ac..e5fe8f136dce 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -6669,6 +6669,7 @@ describe('actions/IOU', () => { .then(async () => { expect(await getOnyxValue(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`)).toStrictEqual({ ...transactionResult, + merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, iouRequestType: CONST.IOU.REQUEST_TYPE.SCAN, }); }); diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 119a01498e98..09d4b75ae889 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -382,11 +382,11 @@ const searchResults: OnyxTypes.SearchResults = { currency: 'USD', hasEReceipt: false, isFromOneTransactionReport: true, - merchant: 'Expenses', + merchant: 'Expense', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expenses', + modifiedMerchant: 'Expense', parentTransactionID: '', pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, reportID, @@ -422,11 +422,11 @@ const searchResults: OnyxTypes.SearchResults = { currency: 'USD', hasEReceipt: false, isFromOneTransactionReport: true, - merchant: 'Expenses', + merchant: 'Expense', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expenses', + modifiedMerchant: 'Expense', parentTransactionID: '', reportID: reportID2, reportType: 'expense', From 38ad7f0e52166a7765cd2dcaca808a20d9812eb0 Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 11 Nov 2025 12:18:13 +0100 Subject: [PATCH 61/65] final one --- tests/unit/Search/SearchUIUtilsTest.ts | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 09d4b75ae889..f55a38f27075 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -800,16 +800,16 @@ const transactionsListItems = [ hasEReceipt: false, isFromOneTransactionReport: true, keyForList: '1', - merchant: 'Expenses', + merchant: 'Expense', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expenses', + modifiedMerchant: 'Expense', parentTransactionID: '', pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, reportID: '123456789', reportType: 'expense', - shouldShowMerchant: true, + shouldShowMerchant: false, shouldShowYear: true, isAmountColumnWide: false, isTaxAmountColumnWide: false, @@ -866,15 +866,15 @@ const transactionsListItems = [ hasEReceipt: false, isFromOneTransactionReport: true, keyForList: '2', - merchant: 'Expenses', + merchant: 'Expense', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expenses', + modifiedMerchant: 'Expense', parentTransactionID: '', reportID: '11111', reportType: 'expense', - shouldShowMerchant: true, + shouldShowMerchant: false, shouldShowYear: true, isAmountColumnWide: false, isTaxAmountColumnWide: false, @@ -954,7 +954,7 @@ const transactionsListItems = [ formattedTotal: 1200, formattedMerchant: '', date: '2025-03-05', - shouldShowMerchant: true, + shouldShowMerchant: false, shouldShowYear: true, keyForList: '3', isAmountColumnWide: false, @@ -1020,7 +1020,7 @@ const transactionsListItems = [ formattedTotal: 3200, formattedMerchant: '', date: '2025-03-05', - shouldShowMerchant: true, + shouldShowMerchant: false, shouldShowYear: true, keyForList: '4', isAmountColumnWide: false, @@ -1105,16 +1105,16 @@ const transactionReportGroupListItems = [ hasEReceipt: false, isFromOneTransactionReport: true, keyForList: '1', - merchant: 'Expenses', + merchant: 'Expense', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expenses', + modifiedMerchant: 'Expense', parentTransactionID: '', pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, reportID: '123456789', reportType: 'expense', - shouldShowMerchant: true, + shouldShowMerchant: false, shouldShowYear: true, isAmountColumnWide: false, isTaxAmountColumnWide: false, @@ -1214,15 +1214,15 @@ const transactionReportGroupListItems = [ ], isFromOneTransactionReport: true, keyForList: '2', - merchant: 'Expenses', + merchant: 'Expense', modifiedAmount: '', modifiedCreated: '', modifiedCurrency: '', - modifiedMerchant: 'Expenses', + modifiedMerchant: 'Expense', parentTransactionID: '', reportID: '11111', reportType: 'expense', - shouldShowMerchant: true, + shouldShowMerchant: false, shouldShowYear: true, isAmountColumnWide: false, isTaxAmountColumnWide: false, From f19bc636995e2ddd919f2a8c00a6a9d75c71dad5 Mon Sep 17 00:00:00 2001 From: alberto Date: Tue, 11 Nov 2025 12:53:26 +0100 Subject: [PATCH 62/65] avoid undefined --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7aba3e05e549..9081a9bbc825 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -14012,7 +14012,7 @@ function updateSplitTransactions({ failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${firstIOU?.childReportID}`, - value: transactionThread, + value: transactionThread ?? null, }); } From c9c9eb6d86321b8555e428138f9a41170195577d Mon Sep 17 00:00:00 2001 From: alberto Date: Wed, 12 Nov 2025 11:51:29 +0100 Subject: [PATCH 63/65] another conflict --- tests/unit/Search/SearchUIUtilsTest.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 9cc93d8a69f0..391f8541fe29 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -1100,12 +1100,7 @@ const transactionReportGroupListItems = [ parentTransactionID: '', pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, reportID: '123456789', -<<<<<<< HEAD - reportType: 'expense', shouldShowMerchant: false, -======= - shouldShowMerchant: true, ->>>>>>> main shouldShowYear: true, isAmountColumnWide: false, isTaxAmountColumnWide: false, From 3c3bc62f1eafc1b94d524e76b7034cf59260c258 Mon Sep 17 00:00:00 2001 From: alberto Date: Fri, 14 Nov 2025 13:30:56 +0100 Subject: [PATCH 64/65] Correctly determine amount for violations --- src/libs/Violations/ViolationsUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 6a04c29eb2d0..8fd2e99e412b 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -11,7 +11,7 @@ import {isReceiptError} from '@libs/ErrorUtils'; import Parser from '@libs/Parser'; import {getDistanceRateCustomUnitRate, getPerDiemRateCustomUnitRate, getSortedTagKeys, isTaxTrackingEnabled} from '@libs/PolicyUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import {shouldShowViolation} from '@libs/TransactionUtils'; +import {hasValidModifiedAmount, shouldShowViolation} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, PolicyCategories, PolicyTagLists, Report, ReportAction, Transaction, TransactionViolation, ViolationName} from '@src/types/onyx'; @@ -325,7 +325,7 @@ const ViolationsUtils = { const isTaxInPolicy = Object.keys(policy.taxRates?.taxes ?? {}).some((key) => key === updatedTransaction.taxCode); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const amount = Number(updatedTransaction.modifiedAmount) || updatedTransaction.amount; + const amount = hasValidModifiedAmount(updatedTransaction) ? Number(updatedTransaction.modifiedAmount) : updatedTransaction.amount; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const currency = updatedTransaction.modifiedCurrency || updatedTransaction.currency; const canCalculateAmountViolations = policy.outputCurrency === currency; From 6ed90550ea65720d63f6cf1d44d9e5f6696e8849 Mon Sep 17 00:00:00 2001 From: alberto Date: Fri, 14 Nov 2025 13:35:51 +0100 Subject: [PATCH 65/65] fix doesReportContainRequestsFromMultipleUsers --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 59a97e327764..8a36059d10d1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11898,7 +11898,7 @@ function hasExportError(reportActions: OnyxEntry | ReportAction[] function doesReportContainRequestsFromMultipleUsers(iouReport: OnyxEntry): boolean { const transactions = getReportTransactions(iouReport?.reportID); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - return isIOUReport(iouReport) && transactions.some((transaction) => (Number(transaction?.modifiedAmount) || transaction?.amount) < 0); + return isIOUReport(iouReport) && transactions.some((transaction) => (Number(transaction?.modifiedAmount) || transaction?.amount) <= 0); } /**