Add desktop notifications for budget threshold crossings

After saving an expense, checks if the category budget reaches 75%,
90%, or 100% thresholds. Sends gio::Notification for each newly
crossed threshold. Tracks sent notifications in database to prevent
duplicates within the same month.
This commit is contained in:
2026-03-02 00:28:43 +02:00
parent 2741df45ad
commit d247c56cfa
3 changed files with 100 additions and 3 deletions

View File

@@ -470,6 +470,58 @@ impl Database {
Ok(())
}
// -- Budget Notifications --
pub fn has_notification_been_sent(
&self,
category_id: i64,
month: &str,
threshold: u32,
) -> SqlResult<bool> {
let count: i64 = self.conn.query_row(
"SELECT COUNT(*) FROM budget_notifications
WHERE category_id = ?1 AND month = ?2 AND threshold = ?3",
params![category_id, month, threshold as i64],
|row| row.get(0),
)?;
Ok(count > 0)
}
pub fn record_notification(
&self,
category_id: i64,
month: &str,
threshold: u32,
) -> SqlResult<()> {
let now = chrono::Utc::now().to_rfc3339();
self.conn.execute(
"INSERT OR IGNORE INTO budget_notifications (category_id, month, threshold, notified_at)
VALUES (?1, ?2, ?3, ?4)",
params![category_id, month, threshold as i64, now],
)?;
Ok(())
}
pub fn check_budget_thresholds(
&self,
category_id: i64,
month: &str,
) -> SqlResult<Vec<u32>> {
let progress = self.get_budget_progress(category_id, month)?;
let mut crossed = Vec::new();
if let Some((_, _, pct)) = progress {
for threshold in [75, 90, 100] {
if pct >= threshold as f64
&& !self.has_notification_been_sent(category_id, month, threshold)?
{
crossed.push(threshold);
}
}
}
Ok(crossed)
}
// -- Exchange Rates --
pub fn get_cached_rate(&self, base: &str, target: &str) -> SqlResult<Option<ExchangeRate>> {