Skip to content

Commit 3e13343

Browse files
authored
Merge pull request #54273 from linhvovan29546/fix/53721-show-merchant-instead-of-distance-rate
fix: track-merchant field is shown instead of distance and rate field
2 parents 683a450 + 485cbd2 commit 3e13343

File tree

2 files changed

+216
-1
lines changed

2 files changed

+216
-1
lines changed

src/libs/actions/IOU.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2698,7 +2698,7 @@ function getTrackExpenseInformation(
26982698
if (!filename) {
26992699
filename = existingTransaction?.filename;
27002700
}
2701-
const isDistanceRequest = existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE;
2701+
const isDistanceRequest = existingTransaction && TransactionUtils.isDistanceRequest(existingTransaction);
27022702
let optimisticTransaction = TransactionUtils.buildOptimisticTransaction({
27032703
existingTransactionID,
27042704
existingTransaction,

tests/actions/IOUTest.ts

+215
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {format} from 'date-fns';
12
import isEqual from 'lodash/isEqual';
23
import type {OnyxCollection, OnyxEntry, OnyxInputValue} from 'react-native-onyx';
34
import Onyx from 'react-native-onyx';
@@ -28,6 +29,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject';
2829
import * as InvoiceData from '../data/Invoice';
2930
import type {InvoiceTestData} from '../data/Invoice';
3031
import createRandomPolicy, {createCategoryTaxExpenseRules} from '../utils/collections/policies';
32+
import createRandomPolicyCategories from '../utils/collections/policyCategory';
3133
import createRandomReport from '../utils/collections/reports';
3234
import createRandomTransaction from '../utils/collections/transaction';
3335
import PusherHelper from '../utils/PusherHelper';
@@ -77,6 +79,219 @@ describe('actions/IOU', () => {
7779
mockFetch = fetch as MockFetch;
7880
return Onyx.clear().then(waitForBatchedUpdates);
7981
});
82+
describe('trackExpense', () => {
83+
it('category a distance expense of selfDM report', async () => {
84+
/*
85+
* This step simulates the following steps:
86+
* - Go to self DM
87+
* - Track a distance expense
88+
* - Go to Troubleshoot > Clear cache and restart > Reset and refresh
89+
* - Go to self DM
90+
* - Click Categorize it (click Upgrade if there is no workspace)
91+
* - Select category and submit the expense to the workspace
92+
*/
93+
94+
// Given a participant of the report
95+
const participant = {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID};
96+
97+
// Given valid waypoints of the transaction
98+
const fakeWayPoints = {
99+
waypoint0: {
100+
keyForList: '88 Kearny Street_1735023533854',
101+
lat: 37.7886378,
102+
lng: -122.4033442,
103+
address: '88 Kearny Street, San Francisco, CA, USA',
104+
name: '88 Kearny Street',
105+
},
106+
waypoint1: {
107+
keyForList: 'Golden Gate Bridge Vista Point_1735023537514',
108+
lat: 37.8077876,
109+
lng: -122.4752007,
110+
address: 'Golden Gate Bridge Vista Point, San Francisco, CA, USA',
111+
name: 'Golden Gate Bridge Vista Point',
112+
},
113+
};
114+
115+
// Given a selfDM report
116+
const selfDMReport = {
117+
...createRandomReport(1),
118+
chatType: CONST.REPORT.CHAT_TYPE.SELF_DM,
119+
};
120+
121+
// Given a policyExpenseChat report
122+
const expenseReport = {
123+
...createRandomReport(1),
124+
chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT,
125+
};
126+
127+
// Given policy categories and a policy
128+
const fakeCategories = createRandomPolicyCategories(3);
129+
const fakePolicy = createRandomPolicy(1);
130+
131+
// Given a transaction with a distance request type and valid waypoints
132+
const fakeTransaction = {
133+
...createRandomTransaction(1),
134+
iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE,
135+
comment: {
136+
...createRandomTransaction(1).comment,
137+
type: CONST.TRANSACTION.TYPE.CUSTOM_UNIT,
138+
customUnit: {
139+
name: CONST.CUSTOM_UNITS.NAME_DISTANCE,
140+
},
141+
waypoints: fakeWayPoints,
142+
},
143+
};
144+
145+
// When the transaction is saved to draft before being submitted
146+
await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${fakeTransaction.transactionID}`, fakeTransaction);
147+
mockFetch?.pause?.();
148+
149+
// When the user submits the transaction to the selfDM report
150+
IOU.trackExpense(
151+
selfDMReport,
152+
fakeTransaction.amount,
153+
fakeTransaction.currency,
154+
format(new Date(), CONST.DATE.FNS_FORMAT_STRING),
155+
fakeTransaction.merchant,
156+
participant.login,
157+
participant.accountID,
158+
participant,
159+
'',
160+
true,
161+
undefined,
162+
'',
163+
undefined,
164+
'',
165+
0,
166+
false,
167+
undefined,
168+
undefined,
169+
undefined,
170+
undefined,
171+
fakeWayPoints,
172+
CONST.IOU.ACTION.CREATE,
173+
fakeTransaction?.actionableWhisperReportActionID,
174+
fakeTransaction?.linkedTrackedExpenseReportAction,
175+
fakeTransaction?.linkedTrackedExpenseReportID,
176+
CONST.CUSTOM_UNITS.FAKE_P2P_ID,
177+
);
178+
await waitForBatchedUpdates();
179+
await mockFetch?.resume?.();
180+
181+
// Given transaction after tracked expense
182+
const transaction = await new Promise<OnyxEntry<OnyxTypes.Transaction>>((resolve) => {
183+
const connection = Onyx.connect({
184+
key: ONYXKEYS.COLLECTION.TRANSACTION,
185+
waitForCollectionCallback: true,
186+
callback: (transactions) => {
187+
Onyx.disconnect(connection);
188+
const trackedExpenseTransaction = Object.values(transactions ?? {}).at(0);
189+
190+
// Then the transaction must remain a distance request
191+
const isDistanceRequest = TransactionUtils.isDistanceRequest(trackedExpenseTransaction);
192+
expect(isDistanceRequest).toBe(true);
193+
resolve(trackedExpenseTransaction);
194+
},
195+
});
196+
});
197+
198+
// Given all report actions of the selfDM report
199+
const allReportActions = await new Promise<OnyxCollection<OnyxTypes.ReportActions>>((resolve) => {
200+
const connection = Onyx.connect({
201+
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
202+
waitForCollectionCallback: true,
203+
callback: (reportActions) => {
204+
Onyx.disconnect(connection);
205+
resolve(reportActions);
206+
},
207+
});
208+
});
209+
210+
// Then the selfDM report should have an actionable track expense whisper action and an IOU action
211+
const selfDMReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${selfDMReport.reportID}`];
212+
expect(Object.values(selfDMReportActions ?? {}).length).toBe(2);
213+
214+
// When the cache is cleared before categorizing the tracked expense
215+
await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction?.transactionID}`, {
216+
iouRequestType: null,
217+
});
218+
219+
// When the transaction is saved to draft by selecting a category in the selfDM report
220+
const reportActionableTrackExpense = Object.values(selfDMReportActions ?? {}).find((reportAction) => ReportActionsUtils.isActionableTrackExpense(reportAction));
221+
ReportUtils.createDraftTransactionAndNavigateToParticipantSelector(
222+
transaction?.transactionID,
223+
selfDMReport.reportID,
224+
CONST.IOU.ACTION.CATEGORIZE,
225+
reportActionableTrackExpense?.reportActionID,
226+
);
227+
await waitForBatchedUpdates();
228+
229+
// Then the transaction draft should be saved successfully
230+
const allTransactionsDraft = await new Promise<OnyxCollection<OnyxTypes.Transaction>>((resolve) => {
231+
const connection = Onyx.connect({
232+
key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT,
233+
waitForCollectionCallback: true,
234+
callback: (transactionDrafts) => {
235+
Onyx.disconnect(connection);
236+
resolve(transactionDrafts);
237+
},
238+
});
239+
});
240+
const transactionDraft = allTransactionsDraft?.[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction?.transactionID}`];
241+
242+
// When the user confirms the category for the tracked expense
243+
IOU.trackExpense(
244+
expenseReport,
245+
transactionDraft?.amount ?? fakeTransaction.amount,
246+
transactionDraft?.currency ?? fakeTransaction.currency,
247+
format(new Date(), CONST.DATE.FNS_FORMAT_STRING),
248+
transactionDraft?.merchant ?? fakeTransaction.merchant,
249+
participant.login,
250+
participant.accountID,
251+
{...participant, isPolicyExpenseChat: true},
252+
'',
253+
false,
254+
undefined,
255+
Object.keys(fakeCategories).at(0) ?? '',
256+
'',
257+
'',
258+
0,
259+
undefined,
260+
fakePolicy,
261+
undefined,
262+
fakeCategories,
263+
undefined,
264+
Object.keys(transactionDraft?.comment?.waypoints ?? {}).length ? TransactionUtils.getValidWaypoints(transactionDraft?.comment?.waypoints, true) : undefined,
265+
CONST.IOU.ACTION.CATEGORIZE,
266+
transactionDraft?.actionableWhisperReportActionID,
267+
transactionDraft?.linkedTrackedExpenseReportAction,
268+
transactionDraft?.linkedTrackedExpenseReportID,
269+
CONST.CUSTOM_UNITS.FAKE_P2P_ID,
270+
);
271+
await waitForBatchedUpdates();
272+
await mockFetch?.resume?.();
273+
274+
// Then the expense should be categorized successfully
275+
await new Promise<void>((resolve) => {
276+
const connection = Onyx.connect({
277+
key: ONYXKEYS.COLLECTION.TRANSACTION,
278+
waitForCollectionCallback: true,
279+
callback: (transactions) => {
280+
Onyx.disconnect(connection);
281+
const categorizedTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction?.transactionID}`];
282+
283+
// Then the transaction must remain a distance request, ensuring that the optimistic data is correctly built and the transaction type remains accurate.
284+
const isDistanceRequest = TransactionUtils.isDistanceRequest(categorizedTransaction);
285+
expect(isDistanceRequest).toBe(true);
286+
287+
// Then the transaction category must match the original category
288+
expect(categorizedTransaction?.category).toBe(Object.keys(fakeCategories).at(0) ?? '');
289+
resolve();
290+
},
291+
});
292+
});
293+
});
294+
});
80295

81296
describe('requestMoney', () => {
82297
it('creates new chat if needed', () => {

0 commit comments

Comments
 (0)