Skip to content

Commit e61f813

Browse files
authored
Merge pull request Expensify#78142 from hoangzinh/77988-fix/preserve-timestamp-partial-approval
[NO QA] Preserve original creation timestamp during partial approval
2 parents 3c0086f + 0533a13 commit e61f813

4 files changed

Lines changed: 81 additions & 4 deletions

File tree

src/libs/ReportUtils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6551,6 +6551,7 @@ function buildOptimisticIOUReport(
65516551
isSendingMoney = false,
65526552
parentReportActionID?: string,
65536553
optimisticIOUReportID?: string,
6554+
createdTimestamp?: string,
65546555
): OptimisticIOUReport {
65556556
const formattedTotal = convertToDisplayString(total, currency);
65566557
const personalDetails = getPersonalDetailsForAccountID(payerAccountID);
@@ -6559,7 +6560,7 @@ function buildOptimisticIOUReport(
65596560
// This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850
65606561
// eslint-disable-next-line @typescript-eslint/no-deprecated
65616562
const policy = getPolicy(policyID);
6562-
const created = DateUtils.getDBTime();
6563+
const created = createdTimestamp ?? DateUtils.getDBTime();
65636564

65646565
const participants: Participants = {
65656566
[payeeAccountID]: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN},
@@ -6730,6 +6731,7 @@ function buildOptimisticExpenseReport(
67306731
nonReimbursableTotal = 0,
67316732
parentReportActionID?: string,
67326733
optimisticIOUReportID?: string,
6734+
createdTimestamp?: string,
67336735
): OptimisticExpenseReport {
67346736
// The amount for Expense reports are stored as negative value in the database
67356737
const storedTotal = total * -1;
@@ -6745,7 +6747,7 @@ function buildOptimisticExpenseReport(
67456747

67466748
const {stateNum, statusNum} = getExpenseReportStateAndStatus(policy);
67476749

6748-
const created = DateUtils.getDBTime();
6750+
const created = createdTimestamp ?? DateUtils.getDBTime();
67496751

67506752
const expenseReport: OptimisticExpenseReport = {
67516753
reportID: optimisticIOUReportID ?? generateReportID(),

src/libs/actions/IOU/index.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9068,6 +9068,7 @@ function getReportFromHoldRequestsOnyxData(
90689068
iouReport: OnyxEntry<OnyxTypes.Report>,
90699069
recipient: Participant,
90709070
policy: OnyxEntry<OnyxTypes.Policy>,
9071+
createdTimestamp?: string,
90719072
): {
90729073
optimisticHoldReportID: string;
90739074
optimisticHoldActionID: string;
@@ -9093,6 +9094,8 @@ function getReportFromHoldRequestsOnyxData(
90939094
iouReport?.currency ?? '',
90949095
holdNonReimbursableAmount,
90959096
newParentReportActionID,
9097+
undefined,
9098+
createdTimestamp,
90969099
)
90979100
: buildOptimisticIOUReport(
90989101
iouReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID,
@@ -9102,6 +9105,8 @@ function getReportFromHoldRequestsOnyxData(
91029105
iouReport?.currency ?? '',
91039106
false,
91049107
newParentReportActionID,
9108+
undefined,
9109+
createdTimestamp,
91059110
);
91069111

91079112
const optimisticExpenseReportPreview = buildOptimisticReportPreview(
@@ -9811,6 +9816,20 @@ function getIOUReportActionToApproveOrPay(chatReport: OnyxEntry<OnyxTypes.Report
98119816
});
98129817
}
98139818

9819+
/**
9820+
* Gets the original creation timestamp from a report's CREATED action or falls back to report.created
9821+
*/
9822+
function getReportOriginalCreationTimestamp(expenseReport?: OnyxEntry<OnyxTypes.Report>): string | undefined {
9823+
if (!expenseReport?.reportID) {
9824+
return undefined;
9825+
}
9826+
9827+
const expenseReportActions = getAllReportActions(expenseReport.reportID);
9828+
const createdAction = Object.values(expenseReportActions ?? {}).find((action) => isCreatedAction(action));
9829+
9830+
return createdAction?.created ?? expenseReport.created;
9831+
}
9832+
98149833
function approveMoneyRequest(
98159834
expenseReport: OnyxEntry<OnyxTypes.Report>,
98169835
policy: OnyxEntry<OnyxTypes.Policy>,
@@ -9994,7 +10013,8 @@ function approveMoneyRequest(
999410013
let optimisticHoldActionID;
999510014
let optimisticHoldReportExpenseActionIDs;
999610015
if (!full && !!chatReport && !!expenseReport) {
9997-
const holdReportOnyxData = getReportFromHoldRequestsOnyxData(chatReport, expenseReport, {accountID: expenseReport.ownerAccountID}, policy);
10016+
const originalCreated = getReportOriginalCreationTimestamp(expenseReport);
10017+
const holdReportOnyxData = getReportFromHoldRequestsOnyxData(chatReport, expenseReport, {accountID: expenseReport.ownerAccountID}, policy, originalCreated);
999810018

999910019
optimisticData.push(...holdReportOnyxData.optimisticData);
1000010020
successData.push(...holdReportOnyxData.successData);
@@ -14364,6 +14384,7 @@ export {
1436414384
detachReceipt,
1436514385
duplicateExpenseTransaction,
1436614386
getIOURequestPolicyID,
14387+
getReportOriginalCreationTimestamp,
1436714388
initMoneyRequest,
1436814389
checkIfScanFileCanBeRead,
1436914390
dismissModalAndOpenReportInInboxTab,

src/pages/home/ReportScreen.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr
248248
const report = useMemo(
249249
() =>
250250
reportOnyx && {
251+
created: reportOnyx.created,
251252
hasParentAccess: reportOnyx.hasParentAccess,
252253
lastReadTime: reportOnyx.lastReadTime,
253254
reportID: reportOnyx.reportID,

tests/actions/IOUTest.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import {renderHook, waitFor} from '@testing-library/react-native';
44
import {format} from 'date-fns';
55
import {deepEqual} from 'fast-equals';
6-
import type {OnyxCollection, OnyxEntry, OnyxInputValue} from 'react-native-onyx';
76
import Onyx from 'react-native-onyx';
7+
import type {OnyxCollection, OnyxEntry, OnyxInputValue, OnyxMultiSetInput} from 'react-native-onyx';
88
import OnyxListItemProvider from '@components/OnyxListItemProvider';
99
import type {SearchQueryJSON, SearchStatus} from '@components/Search/types';
1010
import useOnyx from '@hooks/useOnyx';
@@ -26,6 +26,7 @@ import {
2626
evenlyDistributeSplitExpenseAmounts,
2727
getIOUReportActionToApproveOrPay,
2828
getPerDiemExpenseInformation,
29+
getReportOriginalCreationTimestamp,
2930
getReportPreviewAction,
3031
initMoneyRequest,
3132
initSplitExpense,
@@ -11434,4 +11435,56 @@ describe('actions/IOU', () => {
1143411435
isTransactionDuplicated(mockCashExpenseTransaction, duplicatedTransaction);
1143511436
});
1143611437
});
11438+
11439+
describe('getReportOriginalCreationTimestamp', () => {
11440+
it('should return undefined when report is undefined', () => {
11441+
const result = getReportOriginalCreationTimestamp(undefined);
11442+
expect(result).toBeUndefined();
11443+
});
11444+
11445+
it('should return timestamp from CREATED action when it exists', async () => {
11446+
const createdTimestamp = '2024-01-15 12:00:00.000';
11447+
const report = createRandomReport(1, undefined);
11448+
const reportAction1 = createRandomReportAction(1);
11449+
const reportAction2 = {
11450+
...createRandomReportAction(2),
11451+
actionName: CONST.REPORT.ACTIONS.TYPE.CREATED,
11452+
created: createdTimestamp,
11453+
};
11454+
const reportAction3 = createRandomReportAction(3);
11455+
11456+
await Onyx.multiSet({
11457+
[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report,
11458+
[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {
11459+
[reportAction1.reportActionID]: reportAction1,
11460+
[reportAction2.reportActionID]: reportAction2,
11461+
[reportAction3.reportActionID]: reportAction3,
11462+
},
11463+
} as unknown as OnyxMultiSetInput);
11464+
await waitForBatchedUpdates();
11465+
11466+
const result = getReportOriginalCreationTimestamp(report);
11467+
expect(result).toBe(createdTimestamp);
11468+
});
11469+
11470+
it('should return report.created when CREATED action does not exist', async () => {
11471+
const reportCreatedTimestamp = '2024-01-15 10:00:00.000';
11472+
const report = {
11473+
...createRandomReport(1, undefined),
11474+
created: reportCreatedTimestamp,
11475+
};
11476+
const reportAction1 = createRandomReportAction(1);
11477+
11478+
await Onyx.multiSet({
11479+
[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report,
11480+
[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {
11481+
[reportAction1.reportActionID]: reportAction1,
11482+
},
11483+
} as unknown as OnyxMultiSetInput);
11484+
await waitForBatchedUpdates();
11485+
11486+
const result = getReportOriginalCreationTimestamp(report);
11487+
expect(result).toBe(reportCreatedTimestamp);
11488+
});
11489+
});
1143711490
});

0 commit comments

Comments
 (0)