Skip to content
14 changes: 8 additions & 6 deletions src/libs/actions/IOU/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@
};

let allPersonalDetails: OnyxTypes.PersonalDetailsList = {};
Onyx.connect({

Check warning on line 795 in src/libs/actions/IOU/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.PERSONAL_DETAILS_LIST,
callback: (value) => {
allPersonalDetails = value ?? {};
Expand Down Expand Up @@ -925,7 +925,7 @@
};

let allTransactions: NonNullable<OnyxCollection<OnyxTypes.Transaction>> = {};
Onyx.connect({

Check warning on line 928 in src/libs/actions/IOU/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,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -939,7 +939,7 @@
});

let allTransactionDrafts: NonNullable<OnyxCollection<OnyxTypes.Transaction>> = {};
Onyx.connect({

Check warning on line 942 in src/libs/actions/IOU/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_DRAFT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -948,7 +948,7 @@
});

let allTransactionViolations: NonNullable<OnyxCollection<OnyxTypes.TransactionViolations>> = {};
Onyx.connect({

Check warning on line 951 in src/libs/actions/IOU/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) => {
Expand All @@ -962,7 +962,7 @@
});

let allPolicyTags: OnyxCollection<OnyxTypes.PolicyTagLists> = {};
Onyx.connect({

Check warning on line 965 in src/libs/actions/IOU/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.POLICY_TAGS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -975,7 +975,7 @@
});

let allReports: OnyxCollection<OnyxTypes.Report>;
Onyx.connect({

Check warning on line 978 in src/libs/actions/IOU/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 @@ -984,7 +984,7 @@
});

let allReportNameValuePairs: OnyxCollection<OnyxTypes.ReportNameValuePairs>;
Onyx.connect({

Check warning on line 987 in src/libs/actions/IOU/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_NAME_VALUE_PAIRS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -994,7 +994,7 @@

let deprecatedUserAccountID = -1;
let deprecatedCurrentUserEmail = '';
Onyx.connect({

Check warning on line 997 in src/libs/actions/IOU/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.SESSION,
callback: (value) => {
deprecatedCurrentUserEmail = value?.email ?? '';
Expand All @@ -1003,7 +1003,7 @@
});

let deprecatedCurrentUserPersonalDetails: OnyxEntry<OnyxTypes.PersonalDetails>;
Onyx.connect({

Check warning on line 1006 in src/libs/actions/IOU/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.PERSONAL_DETAILS_LIST,
callback: (value) => {
deprecatedCurrentUserPersonalDetails = value?.[deprecatedUserAccountID] ?? undefined;
Expand All @@ -1011,7 +1011,7 @@
});

let allReportActions: OnyxCollection<OnyxTypes.ReportActions>;
Onyx.connect({

Check warning on line 1014 in src/libs/actions/IOU/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_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand Down Expand Up @@ -11560,7 +11560,12 @@
API.write(WRITE_COMMANDS.PAY_INVOICE, params, onyxData);
}

function detachReceipt(transactionID: string | undefined, transactionPolicy: OnyxEntry<OnyxTypes.Policy>, transactionPolicyCategories?: OnyxEntry<OnyxTypes.PolicyCategories>) {
function detachReceipt(
transactionID: string | undefined,
transactionPolicy: OnyxEntry<OnyxTypes.Policy>,
transactionPolicyTagList: OnyxEntry<OnyxTypes.PolicyTagLists>,
transactionPolicyCategories?: OnyxEntry<OnyxTypes.PolicyCategories>,
) {
if (!transactionID) {
return;
}
Expand Down Expand Up @@ -11616,17 +11621,14 @@
];

if (transactionPolicy && isPaidGroupPolicy(transactionPolicy) && newTransaction) {
// TODO: Replace getPolicyTagsData (https://github.com/Expensify/App/issues/72721) and getPolicyRecentlyUsedTagsData (https://github.com/Expensify/App/issues/71491) with useOnyx hook
// eslint-disable-next-line @typescript-eslint/no-deprecated
const policyTagList = getPolicyTagsData(transactionPolicy.id);
const currentTransactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] ?? [];
const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(
newTransaction,
currentTransactionViolations,
transactionPolicy,
policyTagList ?? {},
transactionPolicyTagList ?? {},
transactionPolicyCategories ?? {},
hasDependentTags(transactionPolicy, policyTagList ?? {}),
hasDependentTags(transactionPolicy, transactionPolicyTagList ?? {}),
isInvoiceReportReportUtils(expenseReport),
);
optimisticData.push(violationsOnyxData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre
// If we have a merge transaction, we need to use the receipt from the merge transaction
const [mergeTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.MERGE_TRANSACTION}${getNonEmptyStringOnyxID(mergeTransactionID)}`);

const [policyTagList] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policy?.id}`);

const isDraftTransaction = !!action;
const draftTransactionID = isDraftTransaction ? transactionID : undefined;

Expand Down Expand Up @@ -255,9 +257,9 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre
* Detach the receipt and close the modal.
*/
const deleteReceiptAndClose = useCallback(() => {
detachReceipt(transaction?.transactionID, policy, policyCategories);
detachReceipt(transaction?.transactionID, policy, policyTagList, policyCategories);
navigation.goBack();
}, [navigation, transaction?.transactionID, policy, policyCategories]);
}, [navigation, transaction?.transactionID, policy, policyCategories, policyTagList]);

/**
* Remove odometer image and close the modal.
Expand Down
115 changes: 115 additions & 0 deletions tests/actions/IOUTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
convertBulkTrackedExpensesToIOU,
createDistanceRequest,
deleteMoneyRequest,
detachReceipt,
getDeleteTrackExpenseInformation,
getIOUReportActionWithBadge,
getReportOriginalCreationTimestamp,
Expand Down Expand Up @@ -130,6 +131,7 @@ import currencyList from '../unit/currencyList.json';
import createPersonalDetails from '../utils/collections/personalDetails';
import createRandomPolicy, {createCategoryTaxExpenseRules} from '../utils/collections/policies';
import createRandomPolicyCategories from '../utils/collections/policyCategory';
import createRandomPolicyTags from '../utils/collections/policyTags';
import createRandomReportAction from '../utils/collections/reportActions';
import {createRandomReport} from '../utils/collections/reports';
import createRandomTransaction from '../utils/collections/transaction';
Expand Down Expand Up @@ -17599,4 +17601,117 @@ describe('actions/IOU', () => {
expect(updatedTransaction?.comment?.odometerEndImage).toBeUndefined();
});
});

describe('detachReceipt', () => {
const transactionID = '1';
const reportID = '2';
const policyID = '3';
const tagListName = 'Department';

const policy = {
...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM),
id: policyID,
};

const policyTagList = createRandomPolicyTags(tagListName, 3);

const transaction = {
...createRandomTransaction(1),
transactionID,
reportID,
receipt: {source: 'receipt-url.jpg'},
merchant: 'Test Merchant',
};

const report = {
...createRandomReport(1, undefined),
reportID,
policyID,
type: CONST.REPORT.TYPE.EXPENSE,
lastVisibleActionCreated: '2024-01-01 00:00:00',
lastReadTime: '2024-01-01 00:00:00',
};

const seedOnyx = async () => {
await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, transaction);
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report);
await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, policy);
await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, policyTagList);
await waitForBatchedUpdates();
};

it('should do nothing when transactionID is undefined', async () => {
const transactionsBefore = await getOnyxValue(ONYXKEYS.COLLECTION.TRANSACTION);

detachReceipt(undefined, undefined, undefined, undefined);
await waitForBatchedUpdates();

const transactionsAfter = await getOnyxValue(ONYXKEYS.COLLECTION.TRANSACTION);
expect(transactionsAfter).toEqual(transactionsBefore);
});

it('should optimistically null the receipt and set pending field', async () => {
// eslint-disable-next-line rulesdir/no-multiple-api-calls
const writeSpy = jest.spyOn(API, 'write').mockImplementation(jest.fn());
await seedOnyx();

try {
detachReceipt(transactionID, undefined, undefined, undefined);
await waitForBatchedUpdates();

const onyxData = writeSpy.mock.calls.at(0)?.at(2) as {optimisticData?: Array<{key: string; value: unknown}>};
const transactionOptimistic = onyxData?.optimisticData?.find((update) => update.key === `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`);
expect(transactionOptimistic?.value).toEqual(
expect.objectContaining({
receipt: null,
pendingFields: {receipt: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
}),
);
} finally {
writeSpy.mockRestore();
}
});

it('should create an optimistic report action and update report timestamps', async () => {
await seedOnyx();

detachReceipt(transactionID, undefined, undefined, undefined);
await waitForBatchedUpdates();

// Then a new report action should be created on the report
const reportActions = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`);
const actions = Object.values(reportActions ?? {});
expect(actions.length).toBeGreaterThan(0);

// And the report timestamps should be updated
const updatedReport = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
expect(updatedReport?.lastVisibleActionCreated).not.toBe('2024-01-01 00:00:00');
});

it('should call API.write with DETACH_RECEIPT command and correct params', async () => {
// eslint-disable-next-line rulesdir/no-multiple-api-calls
const writeSpy = jest.spyOn(API, 'write').mockImplementation(jest.fn());
await seedOnyx();

try {
detachReceipt(transactionID, undefined, undefined, undefined);
await waitForBatchedUpdates();

expect(writeSpy).toHaveBeenCalledWith(WRITE_COMMANDS.DETACH_RECEIPT, expect.objectContaining({transactionID}), expect.anything(), expect.anything());
} finally {
writeSpy.mockRestore();
}
});

it('should compute violations when policy is paid group', async () => {
await seedOnyx();

detachReceipt(transactionID, policy, policyTagList, undefined);
await waitForBatchedUpdates();

const violations = await getOnyxValue(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`);
expect(violations).toBeDefined();
expect(Array.isArray(violations)).toBe(true);
});
});
});
Loading