You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2026-06-23] [$125] Remove tax, total and billable above amount in single expense report #92951
changed the title [-]Remove tax, total and billable above amount in single expense report[/-][+][$125] Remove tax, total and billable above amount in single expense report[/+]on Jun 8, 2026
MoneyReportView treats only the non-reimbursable single-expense case as "single", so Total/Billable/Tax stay rendered for every other single-expense flavor:
What changes do you think we should make in order to solve the problem?
In src/components/ReportActionItem/MoneyReportView.tsx, broaden the single-expense check so it covers any one-transaction report, and gate Total/Billable/Tax + the divider on it:
Then swap the shouldShowTotal guard on the Total row and the divider for shouldShowTotalRow, and restore the missing field-enabled gate so the divider drops too:
In src/components/ReportActionItem/MoneyRequestView.tsx, compute formattedConvertedTaxAmount next to formattedTaxAmount, build taxAmountDescription next to amountDescription using the same shouldShowConvertedAmount check (and skip the suffix when the converted tax is zero so tax-exempt expenses don't render "Converted 0.00"), and pass it as description on the Tax MenuItemWithTopDescription. This keeps the converted-tax info we used to get from the report-level Tax row reachable on the expense itself.
@shawnborton clarified at #92042 (comment) that the intent is to remove all rows above Amount in any single-expense report, not just non-reimbursable ones. The change above generalizes the single-expense check, closes the divider gap from #44695, and keeps converted-tax visible per-expense.
What alternative solutions did you explore? (Optional)
I considered hiding only Total and keeping Billable/Tax, but those rows also just echo per-expense values for one-expense reports and design specifically called them out — partial hiding leaves the same visual noise. Threading isSingleExpenseReport up and overloading the existing shouldShowTotal prop is also tempting, but that prop is owned by callers like ReportActionItemThread and toggling it would silently affect unrelated screens; deriving the flag locally from visibleTransactions keeps the rule scoped to this component. For the divider, gating on sortedPolicyReportFields.length instead of areReportFieldsEnabled was an option, but it misses the case where the policy still has fields configured while the feature is disabled — which is exactly the repro.
To address the issue of removing tax, total, and billable amounts above a single expense report, let's focus on the core problem area in the ExpenseReport.js file. The root cause likely involves unnecessary calculations or display logic that needs to be simplified.
Proposed Solution
Identify the Display Logic:
Locate the section in ExpenseReport.js where tax, total, and billable amounts are calculated and displayed. This is typically found within a function responsible for rendering expense details.
Simplify the Calculation:
Remove or comment out the lines of code that calculate and display these values. Ensure that only the essential information (like the expense description and amount) remains visible.
Update Tests:
If there are associated tests in ExpenseReport.test.js, update them to reflect the changes. Ensure that tests still cover the necessary functionality without relying on the removed calculations.
Diff Summary
- const tax = calculateTax(expense.amount);- const total = expense.amount + tax;- const billable = isBillable(expense);
// Display only essential information
return (
<div>
<p>{expense.description}</p>
<p>Amount: {expense.amount}</p>
</div>
);
Conclusion
By simplifying the display logic in ExpenseReport.js, we directly address the issue without introducing unnecessary complexity. This approach ensures a clean, efficient solution that aligns with best practices and maintains the integrity of the expense reporting system.
Please re-state the problem that we are trying to solve in this issue.
In a single-expense report, the report header still renders report-level rows above the expense amount: Total, Tax, and Billable. These rows duplicate information already available in the single expense detail and leave an unnecessary divider/header area.
What is the root cause of that problem?
MoneyReportView calculates the report-level totals from getMoneyRequestSpendBreakdown() and getBillableAndTaxTotal(), then renders the Total row and breakdown block whenever those values are truthy, regardless of whether the report contains only one transaction:
That works for multi-expense reports, but for a one-expense report those report-level rows repeat the same single expense information. The single-expense detection already exists in isSingleTransactionReport(), but MoneyReportView does not use it:
There is also a related divider issue: shouldShowReportField can remain true based only on report type and report-field list state, even when report fields are disabled. The render block already checks policy?.areReportFieldsEnabled, but the divider gate does not, so the divider can remain visible with no row above it:
What changes do you think we should make in order to solve the problem?
In MoneyReportView, import isSingleTransactionReport and compute the visible transactions consistently with the report UI. Use that to derive an isSingleExpenseReport flag.
Then:
Hide the report-level Total row when isSingleExpenseReport is true.
Hide the entire report-level breakdown block when isSingleExpenseReport is true, including reimbursable/non-reimbursable, Billable, and Tax rows.
Keep the existing breakdown behavior for reports with more than one expense.
Use the same shouldShowTotalRow/shouldShowBreakdown values in the final renderThreadDivider condition, so removing the rows also removes the stray divider.
Add !!policy?.areReportFieldsEnabled to shouldShowReportField, matching the actual report-field render guard.
For the Tax row specifically, move the useful converted-tax context to the expense-level Tax amount row in MoneyRequestView, because the report-level Tax row is removed for single-expense reports. The existing Amount row already appends converted amount details:
Add a similar Tax amount description suffix using transaction.convertedTaxAmount and moneyRequestReport.currency, only when shouldShowConvertedAmount is true and the converted tax amount is non-zero. This preserves the useful converted value without keeping a redundant report-level Tax total.
What specific scenarios should we cover in automated tests to prevent re-opening this issue?
Add/extend UI tests for MoneyReportView:
A single-expense report hides Total.
A single billable expense hides the report-level Billable row.
A single taxed expense hides the report-level Tax row.
Multi-expense reports still show Total, Billable, and Tax rows when applicable.
Disabling report fields hides both report fields and the divider when no other report-level rows are present.
Add/extend UI tests for MoneyRequestView:
A foreign-currency taxed expense appends the converted tax amount to the Tax amount description.
A zero converted tax amount does not render a redundant converted suffix.
Matching expense/report currencies do not render a converted tax suffix.
What alternative solutions did you explore? (Optional)
We could only hide Billable and Tax from the report-level breakdown while leaving Total, but the design discussion clarified that the goal is to remove all items above Amount in the single-expense view. Gating the whole report-level block for single-expense reports is also less brittle than special-casing each row independently.
[Contributor] The offending PR and associated issue have been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.
No offending PR. It's a feature request
[Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.
Link to discussion: N/A
[Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.
[BugZero Assignee] Create a GH issue for creating/updating the regression test once above steps have been agreed upon.
Activity
rlinoz commentedon Jun 8, 2026
@mukhrr for some reason I am not able to assign you here.
[-]Remove tax, total and billable above amount in single expense report[/-][+][$125] Remove tax, total and billable above amount in single expense report[/+]melvin-bot commentedon Jun 8, 2026
David-2002065 commentedon Jun 8, 2026
Proposal
What is the root cause of that problem?
MoneyReportViewtreats only the non-reimbursable single-expense case as "single", so Total/Billable/Tax stay rendered for every other single-expense flavor:isSingleNonReimbursableExpenserequiresreimbursable === false, so a reimbursable single expense isn't classified as single at all — https://github.com/Expensify/App/blob/main/src/components/ReportActionItem/MoneyReportView.tsx#L114billableTotalandtaxTotalare OR-ed in unconditionally, so Billable/Tax survive — https://github.com/Expensify/App/blob/main/src/components/ReportActionItem/MoneyReportView.tsx#L117-L118shouldShowTotalis just a prop with no single-expense awareness, so the Total row above Amount still renders — https://github.com/Expensify/App/blob/main/src/components/ReportActionItem/MoneyReportView.tsx#L234Separately,
shouldShowReportFieldnever checkspolicy.areReportFieldsEnabled. The render block guards on it at https://github.com/Expensify/App/blob/main/src/components/ReportActionItem/MoneyReportView.tsx#L187, but the gate at https://github.com/Expensify/App/blob/main/src/components/ReportActionItem/MoneyReportView.tsx#L155-L159 doesn't — so when fields are disabled the rows hide but the divider at https://github.com/Expensify/App/blob/main/src/components/ReportActionItem/MoneyReportView.tsx#L308 stays on. That's the stray HR @shawnborton flagged once the rows above it were removed.And since we'd be hiding the report-level Tax row, the per-expense Tax field loses its "Converted X" hint for foreign-currency taxed expenses — Amount already has this pattern at https://github.com/Expensify/App/blob/main/src/components/ReportActionItem/MoneyRequestView.tsx#L590-L595 and Tax needs the same.
What changes do you think we should make in order to solve the problem?
In
src/components/ReportActionItem/MoneyReportView.tsx, broaden the single-expense check so it covers any one-transaction report, and gate Total/Billable/Tax + the divider on it:Then swap the
shouldShowTotalguard on the Total row and the divider forshouldShowTotalRow, and restore the missing field-enabled gate so the divider drops too:const shouldShowReportField = !isClosedExpenseReportWithNoExpenses && (isPaidGroupPolicyExpenseReport || isInvoiceReport) && + !!policy?.areReportFieldsEnabled && (!isCombinedReport || !isOnlyTitleFieldEnabled) && !sortedPolicyReportFields.every(shouldHideSingleReportField);In
src/components/ReportActionItem/MoneyRequestView.tsx, computeformattedConvertedTaxAmountnext toformattedTaxAmount, buildtaxAmountDescriptionnext toamountDescriptionusing the sameshouldShowConvertedAmountcheck (and skip the suffix when the converted tax is zero so tax-exempt expenses don't render "Converted 0.00"), and pass it asdescriptionon the TaxMenuItemWithTopDescription. This keeps the converted-tax info we used to get from the report-level Tax row reachable on the expense itself.@shawnborton clarified at #92042 (comment) that the intent is to remove all rows above Amount in any single-expense report, not just non-reimbursable ones. The change above generalizes the single-expense check, closes the divider gap from #44695, and keeps converted-tax visible per-expense.
What alternative solutions did you explore? (Optional)
I considered hiding only Total and keeping Billable/Tax, but those rows also just echo per-expense values for one-expense reports and design specifically called them out — partial hiding leaves the same visual noise. Threading
isSingleExpenseReportup and overloading the existingshouldShowTotalprop is also tempting, but that prop is owned by callers likeReportActionItemThreadand toggling it would silently affect unrelated screens; deriving the flag locally fromvisibleTransactionskeeps the rule scoped to this component. For the divider, gating onsortedPolicyReportFields.lengthinstead ofareReportFieldsEnabledwas an option, but it misses the case where the policy still has fields configured while the feature is disabled — which is exactly the repro.heroking777 commentedon Jun 8, 2026
To address the issue of removing tax, total, and billable amounts above a single expense report, let's focus on the core problem area in the
ExpenseReport.jsfile. The root cause likely involves unnecessary calculations or display logic that needs to be simplified.Proposed Solution
Identify the Display Logic:
Locate the section in
ExpenseReport.jswhere tax, total, and billable amounts are calculated and displayed. This is typically found within a function responsible for rendering expense details.Simplify the Calculation:
Remove or comment out the lines of code that calculate and display these values. Ensure that only the essential information (like the expense description and amount) remains visible.
Update Tests:
If there are associated tests in
ExpenseReport.test.js, update them to reflect the changes. Ensure that tests still cover the necessary functionality without relying on the removed calculations.Diff Summary
Conclusion
By simplifying the display logic in
ExpenseReport.js, we directly address the issue without introducing unnecessary complexity. This approach ensures a clean, efficient solution that aligns with best practices and maintains the integrity of the expense reporting system.mukhrr commentedon Jun 8, 2026
@rlinoz i think you can now
melvin-bot commentedon Jun 8, 2026
Current assignee @hoangzinh is eligible for the External assigner, not assigning anyone new.
KJ21-ENG commentedon Jun 8, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
In a single-expense report, the report header still renders report-level rows above the expense amount: Total, Tax, and Billable. These rows duplicate information already available in the single expense detail and leave an unnecessary divider/header area.
What is the root cause of that problem?
MoneyReportViewcalculates the report-level totals fromgetMoneyRequestSpendBreakdown()andgetBillableAndTaxTotal(), then renders the Total row and breakdown block whenever those values are truthy, regardless of whether the report contains only one transaction:App/src/components/ReportActionItem/MoneyReportView.tsx
Lines 103 to 108 in eaaffe3
App/src/components/ReportActionItem/MoneyReportView.tsx
Lines 222 to 296 in eaaffe3
That works for multi-expense reports, but for a one-expense report those report-level rows repeat the same single expense information. The single-expense detection already exists in
isSingleTransactionReport(), butMoneyReportViewdoes not use it:App/src/libs/MoneyRequestReportUtils.ts
Lines 115 to 121 in eaaffe3
There is also a related divider issue:
shouldShowReportFieldcan remain true based only on report type and report-field list state, even when report fields are disabled. The render block already checkspolicy?.areReportFieldsEnabled, but the divider gate does not, so the divider can remain visible with no row above it:App/src/components/ReportActionItem/MoneyReportView.tsx
Lines 145 to 149 in eaaffe3
What changes do you think we should make in order to solve the problem?
In
MoneyReportView, importisSingleTransactionReportand compute the visible transactions consistently with the report UI. Use that to derive anisSingleExpenseReportflag.Then:
isSingleExpenseReportis true.isSingleExpenseReportis true, including reimbursable/non-reimbursable, Billable, and Tax rows.shouldShowTotalRow/shouldShowBreakdownvalues in the finalrenderThreadDividercondition, so removing the rows also removes the stray divider.!!policy?.areReportFieldsEnabledtoshouldShowReportField, matching the actual report-field render guard.For the Tax row specifically, move the useful converted-tax context to the expense-level Tax amount row in
MoneyRequestView, because the report-level Tax row is removed for single-expense reports. The existing Amount row already appends converted amount details:App/src/components/ReportActionItem/MoneyRequestView.tsx
Lines 566 to 568 in eaaffe3
Add a similar Tax amount description suffix using
transaction.convertedTaxAmountandmoneyRequestReport.currency, only whenshouldShowConvertedAmountis true and the converted tax amount is non-zero. This preserves the useful converted value without keeping a redundant report-level Tax total.What specific scenarios should we cover in automated tests to prevent re-opening this issue?
Add/extend UI tests for
MoneyReportView:Add/extend UI tests for
MoneyRequestView:What alternative solutions did you explore? (Optional)
We could only hide Billable and Tax from the report-level breakdown while leaving Total, but the design discussion clarified that the goal is to remove all items above Amount in the single-expense view. Gating the whole report-level block for single-expense reports is also less brittle than special-casing each row independently.
14 remaining items
melvin-bot commentedon Jun 16, 2026
@hoangzinh Whoops! This issue is 2 days overdue. Let's get this updated quick!
melvin-bot commentedon Jun 18, 2026
@hoangzinh Huh... This is 4 days overdue. Who can take care of this?
melvin-bot commentedon Jun 22, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)melvin-bot commentedon Jun 22, 2026
Payment Summary
Upwork Job
BugZero Checklist (@mallenexpensify)
hoangzinh commentedon Jun 23, 2026
BugZero Checklist:
[Contributor] The offending PR and associated issue have been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.
No offending PR. It's a feature request
[Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.
Link to discussion: N/A
[Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.
[BugZero Assignee] Create a GH issue for creating/updating the regression test once above steps have been agreed upon.
Link to issue: https://github.com/Expensify/Expensify/issues/652653
Regression Test Proposal
Test:
Do we agree 👍 or 👎
Zapier Logs
Run ID: 00040eee-6a58-a110-46d3-7cdaad2e9b54mallenexpensify commentedon Jun 25, 2026
Payment Summary
Contributor: @mukhrr paid $125 via Upwork
Contributor+: @hoangzinh due $125 via NewDot
^ Test case created. Thx
laurenreidexpensify commentedon Jun 29, 2026
$125 approved @hoangzinh
flaviadefaria commentedon Jun 30, 2026
Approved: @mukhrr paid $125 via Upwork