Skip to content

Commit 73ef27e

Browse files
cloudbr34k84claude
andcommitted
fix: null values, rounding, and add transaction_count attribute
- actual now defaults to 0 for budgeted categories with no transactions this month (unbudgeted categories remain None) - over_by defaults to 0 when not over budget (was None) - remaining defaults to 0 when over budget (was None) - percentage_used rounded to 2 decimal places - _fetch_monthly_transactions now returns (amounts, counts) tuple - transaction_count added to enriched category dict and exposed as a sensor attribute Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5fb84fb commit 73ef27e

File tree

3 files changed

+19
-10
lines changed

3 files changed

+19
-10
lines changed

custom_components/ha_pocketsmith/coordinator.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async def _async_update_data(self) -> dict:
5959
uncategorised_count = await self._fetch_uncategorised_count(session, user_id)
6060
categories = await self._fetch_categories(session, user_id)
6161
budget = await self._fetch_budget(session, user_id)
62-
monthly_transactions = await self._fetch_monthly_transactions(session, user_id)
62+
monthly_transactions, monthly_transaction_counts = await self._fetch_monthly_transactions(session, user_id)
6363
monthly_events = await self._fetch_monthly_events(session, user_id)
6464
except aiohttp.ClientError as err:
6565
raise UpdateFailed(
@@ -70,7 +70,7 @@ async def _async_update_data(self) -> dict:
7070
"PocketSmith API request timed out. Check your network connection."
7171
) from err
7272

73-
enriched_categories = self._build_enriched_categories(categories, budget, monthly_transactions, monthly_events)
73+
enriched_categories = self._build_enriched_categories(categories, budget, monthly_transactions, monthly_events, monthly_transaction_counts)
7474

7575
return {
7676
"user_id": user_id,
@@ -80,6 +80,7 @@ async def _async_update_data(self) -> dict:
8080
"categories": categories,
8181
"budget": budget,
8282
"monthly_transactions": monthly_transactions,
83+
"monthly_transaction_counts": monthly_transaction_counts,
8384
"monthly_events": monthly_events,
8485
"enriched_categories": enriched_categories,
8586
"forecast_last_updated": dt_util.utcnow(),
@@ -372,8 +373,8 @@ async def _fetch_budget(self, session: aiohttp.ClientSession, user_id: int) -> l
372373
_LOGGER.debug("Fetched budget for user %s", user_id)
373374
return budget
374375

375-
async def _fetch_monthly_transactions(self, session: aiohttp.ClientSession, user_id: int) -> dict:
376-
"""Return actual spend totals grouped by category ID for the current month."""
376+
async def _fetch_monthly_transactions(self, session: aiohttp.ClientSession, user_id: int) -> tuple[dict, dict]:
377+
"""Return actual spend totals and transaction counts grouped by category ID for the current month."""
377378
today = date.today()
378379
start_date = date(today.year, today.month, 1).isoformat()
379380
last_day = calendar.monthrange(today.year, today.month)[1]
@@ -387,6 +388,7 @@ async def _fetch_monthly_transactions(self, session: aiohttp.ClientSession, user
387388
base_url = "%s/users/%s/transactions" % (_API_BASE, user_id)
388389
url = "%s?start_date=%s&end_date=%s&per_page=1000" % (base_url, start_date, end_date)
389390
totals: dict[int, float] = {}
391+
counts: dict[int, int] = {}
390392
transaction_count = 0
391393

392394
while url:
@@ -447,6 +449,7 @@ async def _fetch_monthly_transactions(self, session: aiohttp.ClientSession, user
447449
cat_id = category.get("id")
448450
amount = t.get("amount", 0)
449451
totals[cat_id] = totals.get(cat_id, 0) + abs(amount)
452+
counts[cat_id] = counts.get(cat_id, 0) + 1
450453
transaction_count += 1
451454

452455
url = next_url
@@ -455,7 +458,7 @@ async def _fetch_monthly_transactions(self, session: aiohttp.ClientSession, user
455458
"Fetched %s monthly transactions across %s categories for user %s",
456459
transaction_count, len(totals), user_id,
457460
)
458-
return totals
461+
return totals, counts
459462

460463
async def _fetch_monthly_events(self, session: aiohttp.ClientSession, user_id: int) -> dict:
461464
"""Return budgeted amount totals grouped by category ID for the current month."""
@@ -533,7 +536,7 @@ async def _fetch_monthly_events(self, session: aiohttp.ClientSession, user_id: i
533536
)
534537
return totals
535538

536-
def _build_enriched_categories(self, categories: list, budget: list, monthly_transactions: dict, monthly_events: dict) -> list:
539+
def _build_enriched_categories(self, categories: list, budget: list, monthly_transactions: dict, monthly_events: dict, monthly_transaction_counts: dict) -> list:
537540
"""Return a flat enriched list of all categories with monthly budget data.
538541
539542
For each non-transfer category:
@@ -580,6 +583,10 @@ def _flatten(cats, flat):
580583
# Actual: sourced from monthly transactions
581584
actual = monthly_transactions.get(cat_id)
582585

586+
# Default actual to 0 for categories that have budget data
587+
if actual is None and cat_id in budget_by_category:
588+
actual = 0
589+
583590
# Budgeted: depends on whether this is a bill category
584591
if cat.get("is_bill"):
585592
budgeted = monthly_events.get(cat_id)
@@ -623,9 +630,9 @@ def _flatten(cats, flat):
623630
b = budgeted or 0
624631
a = actual or 0
625632
over_budget = a > b
626-
over_by = a - b if over_budget else None
627-
remaining = b - a if not over_budget else None
628-
percentage_used = (a / b * 100) if b > 0 else None
633+
over_by = a - b if over_budget else 0
634+
remaining = b - a if not over_budget else 0
635+
percentage_used = round(a / b * 100, 2) if b > 0 else None
629636

630637
result.append({
631638
"category_id": cat_id,
@@ -641,6 +648,7 @@ def _flatten(cats, flat):
641648
"over_budget": over_budget,
642649
"percentage_used": percentage_used,
643650
"currency": currency,
651+
"transaction_count": monthly_transaction_counts.get(cat_id, 0),
644652
})
645653

646654
return result

custom_components/ha_pocketsmith/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
"iot_class": "cloud_polling",
1111
"issue_tracker": "https://github.com/cloudbr34k84/home-assistant-pocketsmith/issues",
1212
"requirements": [],
13-
"version": "0.4.3"
13+
"version": "0.4.4"
1414
}

custom_components/ha_pocketsmith/sensor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ def extra_state_attributes(self) -> dict:
282282
"over_budget": e.get("over_budget"),
283283
"percentage_used": e.get("percentage_used"),
284284
"currency": e.get("currency"),
285+
"transaction_count": e.get("transaction_count"),
285286
}
286287

287288

0 commit comments

Comments
 (0)