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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
};

let deprecatedAllReports: OnyxCollection<Report> = {};
Onyx.connect({

Check warning on line 125 in src/libs/TransactionUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -131,7 +131,7 @@
});

let deprecatedAllTransactionViolations: OnyxCollection<TransactionViolations> = {};
Onyx.connect({

Check warning on line 134 in src/libs/TransactionUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
waitForCollectionCallback: true,
callback: (value) => (deprecatedAllTransactionViolations = value),
Expand Down Expand Up @@ -2248,6 +2248,31 @@
return !!violations?.some((violation) => violation.name === CONST.VIOLATIONS.SMARTSCAN_FAILED);
}

/**
* Check if the initial transaction should be reused for the current file being processed.
*/
function shouldReuseInitialTransaction(
initialTransaction: OnyxEntry<Transaction>,
shouldAcceptMultipleFiles: boolean,
index: number,
isMultiScanEnabled: boolean,
transactions: Transaction[],
): boolean {
if (!initialTransaction) {
return false;
}

if (!shouldAcceptMultipleFiles) {
return true;
}

if (index !== 0) {
return false;
}

return !isMultiScanEnabled || (transactions.length === 1 && (!initialTransaction.receipt?.source || initialTransaction.receipt?.isTestReceipt === true));
}

export {
buildOptimisticTransaction,
calculateTaxAmount,
Expand Down Expand Up @@ -2367,6 +2392,7 @@
mergeProhibitedViolations,
getOriginalAttendees,
getReportOwnerAsAttendee,
shouldReuseInitialTransaction,
};

export type {TransactionChanges};
21 changes: 12 additions & 9 deletions src/pages/iou/request/step/IOURequestStepScan/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
import {isPaidGroupPolicy} from '@libs/PolicyUtils';
import {findSelfDMReportID, generateReportID, getPolicyExpenseChat, isArchivedReport, isPolicyExpenseChat} from '@libs/ReportUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import {getDefaultTaxCode} from '@libs/TransactionUtils';
import {getDefaultTaxCode, shouldReuseInitialTransaction} from '@libs/TransactionUtils';
import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper';
import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound';
import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound';
Expand Down Expand Up @@ -522,7 +522,7 @@
navigateToParticipantPage(iouType, initialTransactionID, reportID);
}
},
[

Check warning on line 525 in src/pages/iou/request/step/IOURequestStepScan/index.native.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useCallback has a missing dependency: 'policyRecentlyUsedCurrencies'. Either include it or remove the dependency array
backTo,
initialTransaction?.isFromGlobalCreate,
initialTransaction?.currency,
Expand Down Expand Up @@ -615,15 +615,18 @@
return;
}

if (!isMultiScanEnabled) {
removeDraftTransactions(true);
}

for (const [index, file] of files.entries()) {
const transaction =
!shouldAcceptMultipleFiles || (index === 0 && transactions.length === 1 && (!initialTransaction?.receipt?.source || initialTransaction?.receipt?.isTestReceipt))
? (initialTransaction as Partial<Transaction>)
: buildOptimisticTransactionAndCreateDraft({
initialTransaction: initialTransaction as Partial<Transaction>,
currentUserPersonalDetails,
reportID,
});
const transaction = shouldReuseInitialTransaction(initialTransaction, shouldAcceptMultipleFiles, index, isMultiScanEnabled, transactions)
? (initialTransaction as Partial<Transaction>)
: buildOptimisticTransactionAndCreateDraft({
initialTransaction: initialTransaction as Partial<Transaction>,
currentUserPersonalDetails,
reportID,
});

const transactionID = transaction.transactionID ?? initialTransactionID;
newReceiptFiles.push({file, source: file.uri ?? '', transactionID});
Expand Down
21 changes: 12 additions & 9 deletions src/pages/iou/request/step/IOURequestStepScan/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
import {isPaidGroupPolicy} from '@libs/PolicyUtils';
import {findSelfDMReportID, generateReportID, getPolicyExpenseChat, isArchivedReport, isPolicyExpenseChat} from '@libs/ReportUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import {getDefaultTaxCode, hasReceipt} from '@libs/TransactionUtils';
import {getDefaultTaxCode, hasReceipt, shouldReuseInitialTransaction} from '@libs/TransactionUtils';
import StepScreenDragAndDropWrapper from '@pages/iou/request/step/StepScreenDragAndDropWrapper';
import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound';
import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound';
Expand Down Expand Up @@ -577,7 +577,7 @@
navigateToParticipantPage(iouType, initialTransactionID, reportID);
}
},
[

Check warning on line 580 in src/pages/iou/request/step/IOURequestStepScan/index.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useCallback has a missing dependency: 'policyRecentlyUsedCurrencies'. Either include it or remove the dependency array
backTo,
initialTransaction?.isFromGlobalCreate,
initialTransaction?.currency,
Expand Down Expand Up @@ -631,16 +631,19 @@
return;
}

if (!isMultiScanEnabled) {
removeDraftTransactions(true);
}

for (const [index, file] of files.entries()) {
const source = URL.createObjectURL(file as Blob);
const transaction =
!shouldAcceptMultipleFiles || (index === 0 && transactions.length === 1 && (!initialTransaction?.receipt?.source || initialTransaction?.receipt?.isTestReceipt))
? (initialTransaction as Partial<Transaction>)
: buildOptimisticTransactionAndCreateDraft({
initialTransaction: initialTransaction as Partial<Transaction>,
currentUserPersonalDetails,
reportID,
});
const transaction = shouldReuseInitialTransaction(initialTransaction, shouldAcceptMultipleFiles, index, isMultiScanEnabled, transactions)
? (initialTransaction as Partial<Transaction>)
: buildOptimisticTransactionAndCreateDraft({
initialTransaction: initialTransaction as Partial<Transaction>,
currentUserPersonalDetails,
reportID,
});

const transactionID = transaction.transactionID ?? initialTransactionID;
newReceiptFiles.push({file, source, transactionID});
Expand Down
52 changes: 52 additions & 0 deletions tests/unit/TransactionUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1334,4 +1334,56 @@ describe('TransactionUtils', () => {
expect(result.at(0)?.accountID).toBe(CURRENT_USER_ID);
});
});

describe('shouldReuseInitialTransaction', () => {
const initialTransaction = generateTransaction({
transactionID: '1',
receipt: {},
});

it('should return false if initialTransaction is missing', () => {
expect(TransactionUtils.shouldReuseInitialTransaction(undefined, true, 0, true, [])).toBe(false);
});

it('should return true if shouldAcceptMultipleFiles is false regardless of index', () => {
expect(TransactionUtils.shouldReuseInitialTransaction(initialTransaction, false, 1, true, [])).toBe(true);
});

it('should return false if index is not 0', () => {
expect(TransactionUtils.shouldReuseInitialTransaction(initialTransaction, true, 1, true, [])).toBe(false);
});

it('should return true if isMultiScanEnabled is false', () => {
expect(TransactionUtils.shouldReuseInitialTransaction(initialTransaction, true, 0, false, [])).toBe(true);
});

it('should return true if isMultiScanEnabled is true, transactions length is 1, and initialTransaction has no receipt source', () => {
const transactionWithNoReceiptSource = generateTransaction({
...initialTransaction,
receipt: {source: ''},
});
expect(TransactionUtils.shouldReuseInitialTransaction(transactionWithNoReceiptSource, true, 0, true, [initialTransaction])).toBe(true);
});

it('should return true if isMultiScanEnabled is true, transactions length is 1, and initialTransaction is a test receipt', () => {
const testReceiptTransaction = generateTransaction({
...initialTransaction,
receipt: {source: 'source', isTestReceipt: true},
});
expect(TransactionUtils.shouldReuseInitialTransaction(testReceiptTransaction, true, 0, true, [initialTransaction])).toBe(true);
});

it('should return false if isMultiScanEnabled is true and transactions length is not 1', () => {
expect(TransactionUtils.shouldReuseInitialTransaction(initialTransaction, true, 0, true, [])).toBe(false);
expect(TransactionUtils.shouldReuseInitialTransaction(initialTransaction, true, 0, true, [initialTransaction, initialTransaction])).toBe(false);
});

it('should return false if isMultiScanEnabled is true, transactions length is 1, and initialTransaction has a receipt source and is not a test receipt', () => {
const transactionWithReceiptSource = generateTransaction({
...initialTransaction,
receipt: {source: 'source'},
});
expect(TransactionUtils.shouldReuseInitialTransaction(transactionWithReceiptSource, true, 0, true, [initialTransaction])).toBe(false);
});
});
});
Loading