Fix: Duplicate review tax code page shows no options across workspaces#81079
Conversation
|
@hungvu193 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] |
Use threadReportID directly in ReviewTaxCode (consistent with other review pages) and derive the policy from the kept transaction's workspace for correct tax lookups. Merge thread transaction's duplicate list in TransactionPreview to ensure cross-workspace duplicates are fully represented in the review flow.
6f1ece5 to
f907dc9
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f1ece5e97
ℹ️ 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".
| const navigateToReviewFields = useCallback(() => { | ||
| Navigation.navigate(getReviewNavigationRoute(Navigation.getActiveRoute(), route.params?.threadReportID, transaction, duplicates, policyCategories, transactionReport)); | ||
| }, [route.params?.threadReportID, transaction, duplicates, policyCategories, transactionReport]); | ||
| const allDups = [...duplicates, ...(threadDupTxns ?? [])]; |
There was a problem hiding this comment.
Filter thread duplicates before merging
duplicates has already been filtered with removeSettledAndApprovedTransactions, but threadDupTxns is appended without that filter. If the thread transaction’s violations include a duplicate that is settled/approved (e.g., one duplicate was already approved), allDups will reintroduce it and getReviewNavigationRoute will store it in reviewDuplicates.duplicates. That can cause the review flow to consider non‑reviewable transactions and pass settled/approved IDs into the merge params. Consider filtering/deduping threadDupTxns (or the merged list) the same way before navigation.
Useful? React with 👍 / 👎.
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
hungvu193
left a comment
There was a problem hiding this comment.
Is this PR ready for review? Could you complete the author checklist please? 👀
| const threadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${route.params?.threadReportID}`]; | ||
| const threadViolations = useTransactionViolations(getTransactionID(threadReport)); | ||
| const [threadDuplicates] = useTransactionsByID( | ||
| useMemo(() => threadViolations?.find((v) => v.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [threadViolations]), |
There was a problem hiding this comment.
I think this file is compiled with react-compiler, we might not need the useMemo here.
| const personalDetails = usePersonalDetails(); | ||
|
|
||
| // Load thread transaction's complete duplicate list for cross-workspace comparison | ||
| const threadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${route.params?.threadReportID}`]; |
There was a problem hiding this comment.
| const threadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${route.params?.threadReportID}`]; | |
| const threadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(route.params?.threadReportID)}`]; |
| const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); | ||
| const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reviewDuplicates?.reportID ?? route.params.threadReportID}`, {canBeMissing: true}); | ||
| const policy = usePolicy(report?.policyID); | ||
| const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.threadReportID}`, {canBeMissing: true}); |
There was a problem hiding this comment.
| const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.threadReportID}`, {canBeMissing: true}); | |
| const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(route.params.threadReportID)}`, {canBeMissing: true}); |
…necessary useMemo
|
Will update checklist soon |
|
Done |
|
@MobileMage Please merge main again |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeScreen.Recording.2026-02-05.at.10.22.39.moviOS: HybridAppios.moviOS: mWeb SafarimSafari.movMacOS: Chrome / SafariScreen.Recording.2026-02-05.at.10.09.38.mov |
|
🚧 @thienlnam has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by https://github.com/thienlnam in version: 9.3.13-1 🚀
|
|
🚀 Deployed to staging by https://github.com/thienlnam in version: 9.3.15-0 🚀
|
Explanation of Change
When reviewing duplicate expenses that span two workspaces with different feature configurations (e.g., Workspace A has taxes/categories/tags enabled, Workspace B does not), the review flow had multiple failures:
ReviewTaxCode.tsxline 27 usedreviewDuplicates?.reportIDas a fallback, loading the kept transaction's report instead of the thread report. This caused all downstream data (transaction, violations, duplicate IDs) to be derived from the wrong source.policywas still derived from the thread report's workspace (which may not have taxes enabled). Tax lookups (getTaxByID,getDefaultTaxCode,getTaxValue) need to use the kept transaction's workspace policy.TransactionPreviewnow also loads the thread transaction's duplicate list and merges both lists before callinggetReviewNavigationRoute, ensuring all duplicates are represented.Changes:
ReviewTaxCode.tsx: Useroute.params.threadReportIDdirectly (consistent with all other review pages). MoveusePolicyto derive fromreviewDuplicatesReport?.policyID(the kept transaction's workspace) instead of the thread report's workspace.TransactionPreview/index.tsx: Load the thread transaction's duplicate list via its violations and merge with the kept transaction's duplicates in the navigation callback, ensuring complete cross-workspace coverage.Fixed Issues
$ #80476
PROPOSAL: #80476 (comment)
Tests
Preconditions:
Offline tests
The duplicate review flow requires online connectivity to load transaction violations and policy data. No offline-specific changes were made.
QA Steps
Same as tests above. Ensure two workspaces exist with different feature configurations (one with taxes/categories/tags enabled, one without) and three duplicate transactions spanning both workspaces.
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
81079-native-a.mp4
Android: mWeb Chrome
81079-web-a.mp4
iOS: Native
81079-native-i.mp4
iOS: mWeb Safari
81079-web-i.mp4
MacOS: Chrome / Safari
81079-web-d.mp4