Skip to content

Commit

Permalink
Merge pull request #50847 from nkdengineer/fix/49801
Browse files Browse the repository at this point in the history
fix: Not here page when create new request
  • Loading branch information
tgolen authored Dec 9, 2024
2 parents 7983984 + 730f3ff commit a6fc5ec
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1805,7 +1805,7 @@ function canAddOrDeleteTransactions(moneyRequestReport: OnyxEntry<Report>): bool
return isAwaitingFirstLevelApproval(moneyRequestReport);
}

if (isReportApproved(moneyRequestReport) || isSettled(moneyRequestReport?.reportID)) {
if (isReportApproved(moneyRequestReport) || isClosedReport(moneyRequestReport) || isSettled(moneyRequestReport?.reportID)) {
return false;
}

Expand Down
12 changes: 12 additions & 0 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7568,6 +7568,10 @@ function cancelPayment(expenseReport: OnyxEntry<OnyxTypes.Report>, chatReport: O
const stateNum: ValueOf<typeof CONST.REPORT.STATE_NUM> = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL ? CONST.REPORT.STATE_NUM.SUBMITTED : CONST.REPORT.STATE_NUM.APPROVED;
const statusNum: ValueOf<typeof CONST.REPORT.STATUS_NUM> = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.APPROVED;
const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, statusNum);
const iouReportActions = ReportActionsUtils.getAllReportActions(chatReport.iouReportID ?? '-1');
const expenseReportActions = ReportActionsUtils.getAllReportActions(expenseReport.reportID ?? '-1');
const iouCreatedAction = Object.values(iouReportActions).find((action) => ReportActionsUtils.isCreatedAction(action));
const expenseCreatedAction = Object.values(expenseReportActions).find((action) => ReportActionsUtils.isCreatedAction(action));
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
Expand All @@ -7579,6 +7583,14 @@ function cancelPayment(expenseReport: OnyxEntry<OnyxTypes.Report>, chatReport: O
},
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`,
value: {
// The report created later will become the iouReportID of the chat report
iouReportID: (iouCreatedAction?.created ?? '') > (expenseCreatedAction?.created ?? '') ? chatReport?.iouReportID : expenseReport.reportID,
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
Expand Down
90 changes: 90 additions & 0 deletions tests/actions/IOUTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,96 @@ describe('actions/IOU', () => {
});
});

describe('a workspace chat with a cancelled payment', () => {
const amount = 10000;
const comment = '💸💸💸💸';
const merchant = 'NASDAQ';

afterEach(() => {
mockFetch?.resume?.();
});

it("has an iouReportID of the cancelled payment's expense report", () => {
let expenseReport: OnyxEntry<OnyxTypes.Report>;
let chatReport: OnyxEntry<OnyxTypes.Report>;

// Given a signed in account, which owns a workspace, and has a policy expense chat
Onyx.set(ONYXKEYS.SESSION, {email: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID});
return waitForBatchedUpdates()
.then(() => {
// Which owns a workspace
PolicyActions.createWorkspace(CARLOS_EMAIL, true, "Carlos's Workspace");
return waitForBatchedUpdates();
})
.then(() =>
TestHelper.getOnyxData({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (allReports) => {
chatReport = Object.values(allReports ?? {}).find((report) => report?.chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT);
},
}),
)
.then(() => {
if (chatReport) {
// When an IOU expense is submitted to that policy expense chat
IOU.requestMoney({
report: chatReport,
participantParams: {
payeeEmail: RORY_EMAIL,
payeeAccountID: RORY_ACCOUNT_ID,
participant: {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID},
},
transactionParams: {
amount,
attendees: [],
currency: CONST.CURRENCY.USD,
created: '',
merchant,
comment,
},
});
}
return waitForBatchedUpdates();
})
.then(() =>
// And given an expense report has now been created which holds the IOU
TestHelper.getOnyxData({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (allReports) => {
expenseReport = Object.values(allReports ?? {}).find((report) => report?.type === CONST.REPORT.TYPE.IOU);
},
}),
)
.then(() => {
// When the expense report is paid elsewhere (but really, any payment option would work)
if (chatReport && expenseReport) {
IOU.payMoneyRequest(CONST.IOU.PAYMENT_TYPE.ELSEWHERE, chatReport, expenseReport);
}
return waitForBatchedUpdates();
})
.then(() => {
if (chatReport && expenseReport) {
// And when the payment is cancelled
IOU.cancelPayment(expenseReport, chatReport);
}
return waitForBatchedUpdates();
})
.then(() =>
TestHelper.getOnyxData({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (allReports) => {
const chatReportData = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`];
// Then the policy expense chat report has the iouReportID of the IOU expense report
expect(chatReportData?.iouReportID).toBe(expenseReport?.reportID);
},
}),
);
});
});

describe('deleteMoneyRequest', () => {
const amount = 10000;
const comment = 'Send me money please';
Expand Down
19 changes: 19 additions & 0 deletions tests/utils/TestHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {fireEvent, screen} from '@testing-library/react-native';
import {Str} from 'expensify-common';
import {Linking} from 'react-native';
import Onyx from 'react-native-onyx';
import type {ConnectOptions} from 'react-native-onyx/dist/types';
import type {ApiCommand, ApiRequestCommandParameters} from '@libs/API/types';
import * as Localize from '@libs/Localize';
import * as Pusher from '@libs/Pusher/pusher';
Expand All @@ -12,6 +13,7 @@ import * as Session from '@src/libs/actions/Session';
import HttpUtils from '@src/libs/HttpUtils';
import * as NumberUtils from '@src/libs/NumberUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type {OnyxKey} from '@src/ONYXKEYS';
import appSetup from '@src/setup';
import type {Response as OnyxResponse, PersonalDetails, Report} from '@src/types/onyx';
import waitForBatchedUpdates from './waitForBatchedUpdates';
Expand All @@ -25,6 +27,9 @@ type MockFetch = jest.MockedFn<typeof fetch> & {
mockAPICommand: <TCommand extends ApiCommand>(command: TCommand, responseHandler: (params: ApiRequestCommandParameters[TCommand]) => OnyxResponse) => void;
};

type ConnectionCallback<TKey extends OnyxKey> = NonNullable<ConnectOptions<TKey>['callback']>;
type ConnectionCallbackParams<TKey extends OnyxKey> = Parameters<ConnectionCallback<TKey>>;

type QueueItem = {
resolve: (value: Partial<Response> | PromiseLike<Partial<Response>>) => void;
input: RequestInfo;
Expand Down Expand Up @@ -65,6 +70,19 @@ function buildPersonalDetails(login: string, accountID: number, firstName = 'Tes
};
}

function getOnyxData<TKey extends OnyxKey>(options: ConnectOptions<TKey>) {
return new Promise<void>((resolve) => {
const connectionID = Onyx.connect({
...options,
callback: (...params: ConnectionCallbackParams<TKey>) => {
Onyx.disconnect(connectionID);
(options.callback as (...args: ConnectionCallbackParams<TKey>) => void)?.(...params);
resolve();
},
});
});
}

/**
* Simulate signing in and make sure all API calls in this flow succeed. Every time we add
* a mockImplementationOnce() we are altering what Network.post() will return.
Expand Down Expand Up @@ -335,4 +353,5 @@ export {
expectAPICommandToHaveBeenCalledWith,
setupGlobalFetchMock,
navigateToSidebarOption,
getOnyxData,
};

0 comments on commit a6fc5ec

Please sign in to comment.