Skip to content

[Exploratory] Web - Taxes - Inconsistent tax amount display on report with negative expenses #98098

Open

Description

@applause-bot

If you haven’t already, check out our contributing guidelines for onboarding. To join our Slack channel, fill out this form.


Version Number: v9.4.51-1
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: https://test-management.browserstack.com/projects/2219752/test-runs/68133792/folder/13176730/87547482/1639586948?sort=%2Bidentifier
Email or phone of affected tester (no customers): leungbratt6+0108@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: Windows 10/Chrome
App Component: Chat Report View

Action Performed:

Preconditions:
User is an Admin of a workspace
Tax settings are enabled
User has created a report with a negative expense and a positive expense with a non-zero tax rate

  1. Open the New Dot app
  2. Navigate to the Workspace chat
  3. Click on the report with 2 expenses
  4. Scroll horizontally to the tax section of the negative expense
  5. Click on the negative expense to open the details

Expected Result:

The Tax amount should display the same on the expense detail and on the report view

Actual Result:

The Tax amount display the positive on the expense detail and as a negative amount on the report view

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome Safari

Screenshots/Videos

Bug7228842_1786177298361.tax-negative.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Julia Ponikarchuk
Bug ID: 7228842

Activity

  1. applause-bot commented on Aug 9, 2026

    @applause-bot
    Author

    While Applause doesn't embed attachments for this bug, here are the links for quick view:
    https://platform.applause.com/services/links/v1/external/8faf6eae15e6a2d0d07cc4527264c3a803e423af452b530ebc7598cfe8dc991d

  2. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Sep 5, 2026
  3. MelvinBot commented on Sep 5, 2026

    @MelvinBot
    Contributor

    Updated 2026-09-11: Rewritten to the solution agreed in this thread — use getTaxAmount and drop the ternary, rather than the sign-reconciliation approach originally proposed. Line references refreshed to b060a99.

    Proposal

    What is the root cause of that problem?

    The expense details page throws away the sign of the tax; the report table keeps it.

    On expense reports, taxAmount is stored in Onyx with the opposite sign of what we display, so a negative expense (refund) has a positive stored taxAmount. getTaxAmount() flips it back for display.

    The Math.abs() is the bug. trjExpensify confirmed Classic shows the refund's tax as negative, and that's the semantic the rest of the codebase already assumes.

    Where the Math.abs came from

    Commit 23ec1dd ("Add Math abs for tax formatting", 2024-06-28), part of #43519 for #42114 — QA had reported tax rendering as negative while moving a track expense into a workspace. The author's stated reason, verbatim:

    I resolved the conflicts and pushed a fix for the taxes showing negative. I don't think taxes will ever be negative, so I added a Math.abs

    So it's a blanket assumption applied to fix one flow; negative expenses were never considered. The sibling Math.abs from that same PR was later narrowed to exactly the flow it was for — useTaxAmount.ts:83 gates it on isMovingTransactionFromTrackExpense. The MoneyRequestView one never got that treatment.

    Why the negative (table) value is the correct one
    • The Total column in the same row renders the refund as negative, via the same sign convention.
    • The report-level tax total adds getTaxAmount(transaction, true) per transaction (ReportUtils.ts:4757, :4770-4775). A refund's tax only reduces the report tax total if it is negative.
    • The details page is inconsistent with itself: its own Amount field renders the signed value (:355), so you see Amount -$20.00 next to Tax $1.82.

    What changes do you think we should make in order to solve the problem?

    1. Collapse the ternary at MoneyRequestView.tsx:371-374 to one line:

    const formattedTaxAmount = convertToDisplayString(transactionTaxAmount ?? 0, actualCurrency);

    transactionTaxAmount is already getTaxAmount() — getTransactionDetails derives it that way (ReportUtils.ts:5022) and is called with displayTransaction, which already resolves updatedTransaction ?? onyxTransaction (:236 → :318 → :340). So the updatedTransaction?.taxAmount !== undefined branch is redundant and wrong — it reads the raw stored (inverted-sign) value.

    Why dropping the if branch is safe, and why keeping it would be inconsistent

    updatedTransaction is only ever passed by the merge and duplicate confirmation pages (TransactionMerge/DynamicConfirmationPage.tsx:231, TransactionDuplicate/DynamicConfirmationPage.tsx:199), so the normal expense-details view always takes the else — fixing only the else does fix the reported bug.

    Both of those flows also store taxAmount in the inverted convention, so getTaxAmount is correct for them too: the duplicate page negates explicitly (:96), and the merge path computes it from the raw stored amount through the sign-preserving calculateTaxAmount (MergeTransactionUtils.ts:804).

    Keep the ternary and fix only the else, and merge/duplicate still renders an unsigned tax next to a signed amount — the same inconsistency, just moved.

    2. Do the same for the converted tax at MoneyRequestView.tsx:376.

    convertedTaxAmount carries the same inverted sign — the report tax total subtracts it (ReportUtils.ts:4774) — so its Math.abs hides the identical bug for foreign-currency expenses. There is no getter for it yet; cleanest is a getConvertedTaxAmount in TransactionUtils mirroring getTaxAmount, exposed through getTransactionDetails alongside convertedAmount, rather than negating inline.

    What alternative solutions did you explore? (Optional)

    Making TaxCell show Math.abs() so the table matches the details page. Rejected: it would put a positive tax next to a negative Total in the same row, and would no longer match the report's own tax total. trjExpensify and Classic both confirm negative is the correct display.

    Adjacent defect found while investigating (separate from this issue)

    The tax edit page renders and submits an absolute value (DynamicIOURequestStepTaxAmountPage.tsx), which then gets negated on store — so editing a refund's tax looks like it silently flips its sign. Traced through the optimistic path only, not confirmed end-to-end against the backend response.

    Verification notes

    Static analysis against b060a99. I could not drive the app in a browser in this run, so the rendered strings are unconfirmed by me — but the reporters' videos and the code path agree.

    Note the Tax column is only rendered by the wide table row, so the mismatch is visible on desktop/wide layouts.


    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/98098) to create a draft PR.
    To refine: @MelvinBot <your feedback>
    To reject: Explain why you are rejecting Melvin's proposal.

  4. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Sep 5, 2026
  5. melvin-bot commented on Sep 5, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @bernhardoj (External)

  6. melvin-bot commented on Sep 5, 2026

    @melvin-bot

    Unable to auto-create job on Upwork. The BZ team member should create it manually for this issue.

  7. bernhardoj commented on Sep 6, 2026

    @bernhardoj
    Contributor

    @jponikarchuk is this still reproducible? I can't seem to reproduce it

  8. 67 remaining items

  9. added
    Awaiting PaymentAuto-added when associated PR is deployed to production
    and removed on Oct 19, 2026
  10. melvin-bot commented on Oct 19, 2026

    @melvin-bot

    Triggered auto assignment to @mallenexpensify (Awaiting Payment)

  11. melvin-bot commented on Oct 19, 2026

    @melvin-bot

    Payment Summary

    Resolving PRs:

    BugZero Checklist (@mallenexpensify)

    • I have confirmed assignees, roles, and Upwork contracts look correct
    • I have paid out Upwork contracts / manual NewDot requests
    • [BugZero Assignee] I have created a GH issue for creating/updating the regression test once above steps have been agreed upon
  12. removed
    Awaiting PaymentAuto-added when associated PR is deployed to production
    on Oct 20, 2026
  13. changed the title [-][Due for payment 2026-10-20] [Exploratory] Web - Taxes - Inconsistent tax amount display on report with negative expenses[/-] [+][Exploratory] Web - Taxes - Inconsistent tax amount display on report with negative expenses[/+] on Oct 20, 2026
  14. removed their assignment
    on Oct 20, 2026
  15. mallenexpensify commented on Oct 20, 2026

    @mallenexpensify
    Contributor

    Please comment and tag me if/when payment is due, thx

  16. melvin-bot commented on Oct 27, 2026

    @melvin-bot

    @grgia, @bernhardoj Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

  17. melvin-bot commented on Oct 29, 2026

    @melvin-bot

    @grgia, @bernhardoj Huh... This is 4 days overdue. Who can take care of this?

  18. melvin-bot commented on Nov 2, 2026

    @melvin-bot

    @grgia, @bernhardoj 8 days overdue is a lot. Should this be a Weekly issue? If so, feel free to change it!

  19. bernhardoj commented on Nov 3, 2026

    @bernhardoj
    Contributor

    Not overdue.

    @grgia how is the BE fix of this going?

  20. melvin-bot commented on Nov 3, 2026

    @melvin-bot

    @grgia, @bernhardoj 8 days overdue is a lot. Should this be a Weekly issue? If so, feel free to change it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorNot a priorityReviewingHas a PR in review

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions