- Implement subscriptions view with bidirectional recurring transaction sync - Add cascade delete/pause/resume between subscriptions and recurring - Fix foreign key constraints when deleting recurring transactions - Add cross-view instant refresh via callback pattern - Replace Bezier chart smoothing with Fritsch-Carlson monotone Hermite interpolation - Smooth budget sparklines using shared monotone_subdivide function - Add vertical spacing to budget rows - Add app icon (receipt on GNOME blue) in all sizes for desktop, web, and AppImage - Add calendar, credit cards, forecast, goals, insights, and wishlist views - Add date picker, numpad, quick-add, category combo, and edit dialog components - Add import/export for CSV, JSON, OFX, QIF formats - Add NLP transaction parsing, OCR receipt scanning, expression evaluator - Add notification support, Sankey chart, tray icon - Add demo data seeder with full DB wipe - Expand database schema with subscriptions, goals, credit cards, and more
60 lines
2.0 KiB
Rust
60 lines
2.0 KiB
Rust
use crate::db::Database;
|
|
use crate::export_json::ExportData;
|
|
use crate::models::{NewCategory, NewTransaction};
|
|
use std::path::Path;
|
|
|
|
pub fn import_json(db: &Database, path: &Path, merge: bool) -> Result<usize, Box<dyn std::error::Error>> {
|
|
let content = std::fs::read_to_string(path)?;
|
|
let data: ExportData = serde_json::from_str(&content)?;
|
|
|
|
if !merge {
|
|
db.reset_all_data()?;
|
|
}
|
|
|
|
for cat in &data.categories {
|
|
let existing = db.list_categories(Some(cat.transaction_type))?;
|
|
if !existing.iter().any(|c| c.name == cat.name) {
|
|
let new_cat = NewCategory {
|
|
name: cat.name.clone(),
|
|
icon: cat.icon.clone(),
|
|
color: cat.color.clone(),
|
|
transaction_type: cat.transaction_type,
|
|
sort_order: cat.sort_order,
|
|
parent_id: None,
|
|
};
|
|
db.insert_category(&new_cat)?;
|
|
}
|
|
}
|
|
|
|
let mut count = 0;
|
|
for txn in &data.transactions {
|
|
let categories = db.list_categories(Some(txn.transaction_type))?;
|
|
let original_cat = data.categories.iter().find(|c| c.id == txn.category_id);
|
|
let category_id = match original_cat {
|
|
Some(oc) => categories.iter().find(|c| c.name == oc.name).map(|c| c.id),
|
|
None => None,
|
|
};
|
|
let Some(category_id) = category_id else { continue };
|
|
|
|
if merge && db.find_duplicate_transaction(txn.amount, txn.transaction_type, category_id, txn.date)? {
|
|
continue;
|
|
}
|
|
|
|
let new_txn = NewTransaction {
|
|
amount: txn.amount,
|
|
transaction_type: txn.transaction_type,
|
|
category_id,
|
|
currency: txn.currency.clone(),
|
|
exchange_rate: txn.exchange_rate,
|
|
note: txn.note.clone(),
|
|
date: txn.date,
|
|
recurring_id: None,
|
|
payee: txn.payee.clone(),
|
|
};
|
|
db.insert_transaction(&new_txn)?;
|
|
count += 1;
|
|
}
|
|
|
|
Ok(count)
|
|
}
|