fix: exclude deferred transactions from expected totals in Balance View and ledger calculations #233

Merged
Copilot merged 2 commits from copilot/debug-deferred-amount-calculation into main 2026-04-26 05:57:49 +00:00
Copilot commented 2026-04-26 05:31:44 +00:00 (Migrated from github.com)

Deferred transactions were being counted in both the "Expected" total and the separate "Deferred" breakdown on the Balance View page and ledger view — inflating Expected by the full deferred amount (e.g. 3 667 kr too high).

Description

DeferredTransaction DTOs have expected_amount set and actual_amount = null. Several frontend functions accumulated expected amounts without guarding against deferred state:

// Before — deferred transactions pass this check
if (t.expected_amount !== null)
    expected += parseFloat(t.expected_amount) || 0;

// After — mirrors BalanceService.php: ! $t->isDeferred()
if (!t.is_deferred && t.expected_amount !== null)
    expected += parseFloat(t.expected_amount) || 0;

The following components were affected and have been fixed:

  • CategorySummaryList.vueexpectedTotal accumulation: drives the "Category Total: Expected" and "Grand Total: Expected" display on the Balance View page
  • MonthView.vue groupByCategory — expected dataset used by the Balance View donut charts
  • CombinedMonthlyAccordion.vue calculateSectionTotals — used by getIncomeSectionTotals, getExpenseSectionTotals, and monthlyTotals (affects section headers and month-level expected/net totals on the ledger view)
  • CombinedMonthlyAccordion.vue categoryTotals — used by CategoryTotalsRow (affects the per-category "Expected" display on the ledger view)

All four now skip is_deferred transactions when accumulating expected, consistent with how BalanceService.php has always handled this server-side.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (code change that neither fixes a bug nor adds a feature)
  • Performance improvement
  • Test coverage improvement

Testing

  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have tested this manually (if applicable)

Code Quality

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings or errors

CI/CD Checks

  • All automated tests pass
  • Code formatting (Pint) passes
  • JavaScript/TypeScript linting passes
  • Build process completes successfully

Additional Notes

No backend changes required — BalanceService.php already excluded deferred transactions from $expectedExpenses via ! $t->isDeferred(). This fix brings all affected frontend calculations in line with that existing behaviour.

Deferred transactions were being counted in both the "Expected" total and the separate "Deferred" breakdown on the Balance View page and ledger view — inflating Expected by the full deferred amount (e.g. 3 667 kr too high). ## Description `DeferredTransaction` DTOs have `expected_amount` set and `actual_amount = null`. Several frontend functions accumulated expected amounts without guarding against deferred state: ```js // Before — deferred transactions pass this check if (t.expected_amount !== null) expected += parseFloat(t.expected_amount) || 0; // After — mirrors BalanceService.php: ! $t->isDeferred() if (!t.is_deferred && t.expected_amount !== null) expected += parseFloat(t.expected_amount) || 0; ``` The following components were affected and have been fixed: - **`CategorySummaryList.vue`** — `expectedTotal` accumulation: drives the "Category Total: Expected" and "Grand Total: Expected" display on the **Balance View page** - **`MonthView.vue`** `groupByCategory` — expected dataset used by the **Balance View donut charts** - **`CombinedMonthlyAccordion.vue`** `calculateSectionTotals` — used by `getIncomeSectionTotals`, `getExpenseSectionTotals`, and `monthlyTotals` (affects section headers and month-level expected/net totals on the ledger view) - **`CombinedMonthlyAccordion.vue`** `categoryTotals` — used by `CategoryTotalsRow` (affects the per-category "Expected" display on the ledger view) All four now skip `is_deferred` transactions when accumulating expected, consistent with how `BalanceService.php` has always handled this server-side. ## Type of Change - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update - [ ] Refactoring (code change that neither fixes a bug nor adds a feature) - [ ] Performance improvement - [ ] Test coverage improvement ## Testing - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] I have tested this manually (if applicable) ## Code Quality - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [x] My changes generate no new warnings or errors ## CI/CD Checks - [ ] All automated tests pass - [ ] Code formatting (Pint) passes - [x] JavaScript/TypeScript linting passes - [ ] Build process completes successfully ## Additional Notes No backend changes required — `BalanceService.php` already excluded deferred transactions from `$expectedExpenses` via `! $t->isDeferred()`. This fix brings all affected frontend calculations in line with that existing behaviour.
niddelicious (Migrated from github.com) reviewed 2026-04-26 05:31:44 +00:00
coderabbitai[bot] commented 2026-04-26 05:31:52 +00:00 (Migrated from github.com)

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 25d23130-3b29-4e60-aaa2-a46a1f8c63b3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands and usage tips.

<!-- This is an auto-generated comment: summarize by coderabbit.ai --> <!-- This is an auto-generated comment: skip review by coderabbit.ai --> > [!IMPORTANT] > ## Review skipped > > Bot user detected. > > To trigger a single review, invoke the `@coderabbitai review` command. > > <details> > <summary>⚙️ Run configuration</summary> > > **Configuration used**: defaults > > **Review profile**: CHILL > > **Plan**: Free > > **Run ID**: `25d23130-3b29-4e60-aaa2-a46a1f8c63b3` > > </details> > > You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file. > > Use the checkbox below for a quick retry: > - [ ] <!-- {"checkboxId": "e9bb8d72-00e8-4f67-9cb2-caf3b22574fe"} --> 🔍 Trigger review <!-- end of auto-generated comment: skip review by coderabbit.ai --> <!-- tips_start --> --- <sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub> <!-- tips_end -->
niddelicious commented 2026-04-26 05:34:38 +00:00 (Migrated from github.com)

@copilot
I thought it was the Balance View page that was incorrect and showing too high of an expected amount?

@copilot I thought it was the Balance View page that was incorrect and showing too high of an expected amount?
copilot-pull-request-reviewer[bot] (Migrated from github.com) reviewed 2026-04-26 05:38:38 +00:00
copilot-pull-request-reviewer[bot] (Migrated from github.com) left a comment

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Fixes ledger “Expected” totals being overstated by excluding deferred transactions from expected-amount rollups, aligning frontend calculations with existing backend behavior.

Changes:

  • Skip is_deferred transactions when accumulating expected in section totals.
  • Skip is_deferred transactions when accumulating expected in per-category totals.
  • Document the bug fix in DEVLOG/CHANGELOG.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.

File Description
resources/js/Pages/Transactions/Partials/CombinedMonthlyAccordion.vue Excludes deferred transactions from expected-total aggregation in two calculation helpers.
DEVLOG.md Adds an unreleased entry describing the deferred/expected double-counting fix.
CHANGELOG.md Adds a user-facing bug-fix entry noting deferred expenses no longer inflate expected totals.
## Pull request overview > [!NOTE] > Copilot was unable to run its full agentic suite in this review. Fixes ledger “Expected” totals being overstated by excluding deferred transactions from expected-amount rollups, aligning frontend calculations with existing backend behavior. **Changes:** - Skip `is_deferred` transactions when accumulating `expected` in section totals. - Skip `is_deferred` transactions when accumulating `expected` in per-category totals. - Document the bug fix in DEVLOG/CHANGELOG. ### Reviewed changes Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments. | File | Description | | ---- | ----------- | | resources/js/Pages/Transactions/Partials/CombinedMonthlyAccordion.vue | Excludes deferred transactions from expected-total aggregation in two calculation helpers. | | DEVLOG.md | Adds an unreleased entry describing the deferred/expected double-counting fix. | | CHANGELOG.md | Adds a user-facing bug-fix entry noting deferred expenses no longer inflate expected totals. |
@ -10,6 +10,9 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and
### Bug Fixes
#### 💸 Deferred Expenses No Longer Inflate the Expected Total
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-26 05:38:38 +00:00

Similar to DEVLOG: this entry says 'Deferred Expenses' but the fix is implemented at the transaction aggregation layer and may affect any deferred transaction shown in the ledger view. Consider changing this to 'Deferred transactions…' (or explicitly stating it's expenses-only if that’s guaranteed by the data model for this view).

Similar to DEVLOG: this entry says 'Deferred Expenses' but the fix is implemented at the transaction aggregation layer and may affect any deferred transaction shown in the ledger view. Consider changing this to 'Deferred transactions…' (or explicitly stating it's expenses-only if that’s guaranteed by the data model for this view).
@ -8,6 +8,14 @@ Use `[1.CURRENT]` as the version heading for unreleased changes. This section mu
## [1.CURRENT]
### 🐛 Fix: Deferred Expenses Inflating Expected Totals
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-26 05:38:38 +00:00

The PR description/title refer to 'deferred transactions' (not only expenses), and the code change applies to shared section/category aggregation logic that may include both income and expenses. Consider renaming this heading (and related wording below) to 'Deferred transactions…' to match the actual scope and avoid confusion.

The PR description/title refer to 'deferred transactions' (not only expenses), and the code change applies to shared section/category aggregation logic that may include both income and expenses. Consider renaming this heading (and related wording below) to 'Deferred transactions…' to match the actual scope and avoid confusion.
@ -431,3 +431,3 @@
if (t.expected_amount !== null)
if (!t.is_deferred && t.expected_amount !== null)
expected += parseFloat(t.expected_amount) || 0;
if (t.actual_amount !== null)
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-26 05:38:37 +00:00

This relies on t.is_deferred being a real boolean (or numeric 0/1). If the API ever serializes it as a string (e.g. '0'/'1'), !t.is_deferred will treat '0' as truthy and incorrectly exclude non-deferred transactions from expected. Consider normalizing the DTO field to a boolean when ingesting data (or using a stricter deferred check like t.is_deferred === true / === 1) and then using that normalized value here.

This relies on `t.is_deferred` being a real boolean (or numeric 0/1). If the API ever serializes it as a string (e.g. `'0'`/`'1'`), `!t.is_deferred` will treat `'0'` as truthy and incorrectly exclude non-deferred transactions from `expected`. Consider normalizing the DTO field to a boolean when ingesting data (or using a stricter deferred check like `t.is_deferred === true` / `=== 1`) and then using that normalized value here.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-26 05:38:38 +00:00

Same concern as the section totals: if is_deferred can be '0'/'1' (string) rather than boolean/number, this condition will misclassify transactions and undercount expected. Normalize is_deferred once (preferred) or apply a strict deferred predicate here to avoid relying on JS truthiness.

Same concern as the section totals: if `is_deferred` can be `'0'`/`'1'` (string) rather than boolean/number, this condition will misclassify transactions and undercount `expected`. Normalize `is_deferred` once (preferred) or apply a strict deferred predicate here to avoid relying on JS truthiness.
Copilot commented 2026-04-26 05:39:42 +00:00 (Migrated from github.com)

@copilot
I thought it was the Balance View page that was incorrect and showing too high of an expected amount?

You're right — the same bug was present in two components that back the Balance View page:

  • CategorySummaryList.vue — the expectedTotal accumulation (drives the "Category Total: Expected" display and the Grand Total Expected row)
  • MonthView.vue groupByCategory — the expected data used by the donut charts

Both now skip deferred transactions when accumulating expected (commit 1b659e2). The previous commit only fixed the ledger-side components (CombinedMonthlyAccordion.vue), not the Balance View ones.

> @copilot > I thought it was the Balance View page that was incorrect and showing too high of an expected amount? You're right — the same bug was present in two components that back the Balance View page: - **`CategorySummaryList.vue`** — the `expectedTotal` accumulation (drives the "Category Total: Expected" display and the Grand Total Expected row) - **`MonthView.vue`** `groupByCategory` — the expected data used by the donut charts Both now skip deferred transactions when accumulating expected (commit `1b659e2`). The previous commit only fixed the ledger-side components (`CombinedMonthlyAccordion.vue`), not the Balance View ones.
niddelicious commented 2026-04-26 06:12:05 +00:00 (Migrated from github.com)

@copilot
The Balance View page, where individual months are broken down, still seem to have issues both in the category totals and grand total

@copilot The Balance View page, where individual months are broken down, still seem to have issues both in the category totals and grand total
Sign in to join this conversation.
No description provided.