-
Notifications
You must be signed in to change notification settings - Fork 3.5k
fix: preserve split child transactions when report is deleted #79192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix: preserve split child transactions when report is deleted #79192
Conversation
|
@ikevin127 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
All contributors have signed the CLA ✍️ ✅ |
e8b7e6f to
44898ed
Compare
|
I have read the CLA Document and I hereby sign the CLA |
44898ed to
d91dccc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e8b7e6f489
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
src/libs/TransactionUtils/index.ts
Outdated
| const isSplitChild = currentTransaction?.comment?.originalTransactionID === originalTransactionID; | ||
| // For split children, include them even if their report was deleted. | ||
| // The split relationship is defined by originalTransactionID, not the report. | ||
| return ( | ||
| currentTransaction?.comment?.originalTransactionID === originalTransactionID && | ||
| !!currentReport && | ||
| isSplitChild && | ||
| (!!currentReport || currentTransaction?.comment?.source === CONST.IOU.TYPE.SPLIT) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard split children with missing report actions
Because this now includes split children even when their report is gone, getChildTransactions can return transactions whose report (and report actions) were cleared by deleteReport. In updateSplitTransactions, those transactions can fall into undeletedTransactions, and getDeleteTrackExpenseInformation immediately dereferences reportAction.reportActionID/childReportID; if the report actions were nulled, currentReportAction is undefined and this path will throw when a user removes that split from the edit list. Consider filtering out split children whose report/actions are missing or guarding currentReportAction before attempting a delete.
Useful? React with 👍 / 👎.
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
1c17eb8 to
f5449f7
Compare
src/libs/TransactionUtils/index.ts
Outdated
| // Include split children even if their report was deleted, but exclude explicitly orphaned | ||
| // transactions (reportID '0'). The split relationship is defined by originalTransactionID. | ||
| const hasValidReportID = !!currentTransaction?.reportID && currentTransaction.reportID !== '0'; | ||
| return ( |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
src/libs/TransactionUtils/index.ts
Outdated
| const isSplitChild = currentTransaction?.comment?.originalTransactionID === originalTransactionID; | ||
| // Include split children even if their report was deleted, but exclude explicitly orphaned | ||
| // transactions (reportID '0'). The split relationship is defined by originalTransactionID. | ||
| const hasValidReportID = !!currentTransaction?.reportID && currentTransaction.reportID !== '0'; |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppandroid-hybrid.mp4Android: mWeb Chromeandroid-mweb.mp4iOS: HybridAppios-hybrid.moviOS: mWeb Safariios-mweb.movMacOS: Chrome / Safariweb.mov |
|
🟡 NAB - Missing Test Case (Minor) Consider adding a test for transactions where source is NOT it('should exclude non-split transactions when their report was deleted', () => {
const childTransaction = generateTransaction({
transactionID: 'child-non-split',
reportID: 'deleted-report-id',
comment: {
originalTransactionID,
source: undefined, // Not a split
},
});
const transactions = {
[`${ONYXKEYS.COLLECTION.TRANSACTION}child-non-split`]: childTransaction,
};
const result = TransactionUtils.getChildTransactions(transactions, reportCollectionDataSet, originalTransactionID);
expect(result).toHaveLength(0);
});Why: This ensures the fallback behavior for non-split child transactions (requiring |
This comment was marked as outdated.
This comment was marked as outdated.
b44e152 to
f56dd4f
Compare
When a report containing a split expense child was deleted, the getChildTransactions function excluded the child transaction because it required the report to exist. This caused incorrect isReverseSplitOperation determination, which reverted the split and cleared split metadata. The fix adds an optional includeOrphaned parameter to getChildTransactions: - Default (false): excludes orphaned transactions (reportID '0') for processing - When true: includes all children for accurate counting In updateSplitTransactions, we compare counts to detect orphaned children and prevent incorrect split reversal. Fixed Issues: Expensify#78933
f56dd4f to
a529d10
Compare
|
@MobileMage Thanks for the updates - the fix works now, reviewing the code once again since there's been significant changes 🔄 Tip As a tip please try to not force-push anymore as it interferes with branch history and makes things difficult for reviewer and also in case a revert is required (here's a paragraph from the guidelines where this is mentioned:
It's okay if you have multiple commits - that's better than force-pushing since it doesn't mess with the PR commit history 👍 |
src/libs/actions/IOU/index.ts
Outdated
| const hasEditableSplitExpensesLeft = splitExpenses.some((expense) => (expense.statusNum ?? 0) < CONST.REPORT.STATUS_NUM.SUBMITTED); | ||
| const isReverseSplitOperation = splitExpenses.length === 1 && originalChildTransactions.length > 0 && hasEditableSplitExpensesLeft; | ||
| // Don't revert split if there are orphaned children (reportID '0') - they're still part of the split | ||
| const allChildTransactions = getChildTransactions(allTransactionsList, allReportsList, originalTransactionID, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple lines above (line 11956) we already call getChildTransactions without the includeOrphaned param.
Issue: This iterates over all transactions twice - for large transaction lists, this will impact performance.
Suggested Fix:
// Get all children once (including orphaned), then filter for non-orphaned
const allChildTransactions = getChildTransactions(allTransactionsList, allReportsList, originalTransactionID, true);
const originalChildTransactions = allChildTransactions.filter(
(tx) => tx?.reportID !== CONST.REPORT.UNREPORTED_REPORT_ID
);
const isCreationOfSplits = originalChildTransactions.length === 0;
const hasEditableSplitExpensesLeft = splitExpenses.some((expense) => (expense.statusNum ?? 0) < CONST.REPORT.STATUS_NUM.SUBMITTED);
// Don't revert split if there are orphaned children (reportID '0') - they're still part of the split
const isReverseSplitOperation =
splitExpenses.length === 1 && originalChildTransactions.length > 0 && hasEditableSplitExpensesLeft && allChildTransactions.length === originalChildTransactions.length;Bug if Not Addressed: In workspaces with thousands of transactions, users may experience noticeable lag when editing split expenses due to redundant iteration.
|
✅ Completed PR Reviewer Checklist. Awaiting issues to be addressed by PR author before moving forward to approving.
|
|
Thanks for the review @ikevin127! Re: Force-pushing - Noted, I'll avoid force-pushing during reviews going forward. Thanks for the tip! Re: Performance issue - Good catch! Fixed in the latest commit ( const allChildTransactions = getChildTransactions(..., true);
const originalChildTransactions = allChildTransactions.filter(
(tx) => tx?.reportID !== CONST.REPORT.UNREPORTED_REPORT_ID
);Re: Recordings - I'll get the platform recordings done today. |
Explanation of Change
When a report containing a split expense child was deleted, the
getChildTransactionsfunction excluded the child transaction because it required the report to exist (!!currentReport). This caused incorrectisReverseSplitOperationdetermination, which reverted the split and cleared split metadata.The fix includes split children (identified by
comment.source === CONST.IOU.TYPE.SPLIT) even if their report was deleted, since the split relationship is defined byoriginalTransactionID, not the report.Fixed Issues
$ #78933
PROPOSAL: #78933 (comment)
Tests
Offline tests
N/A - This fix affects transaction querying logic that doesn't have offline-specific behavior.
QA Steps
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari