Merge pull request 'database-setup' (#3) from database-setup into master

Reviewed-on: finwise/finewise#3
This commit was merged in pull request #3.
This commit is contained in:
2026-02-16 11:08:05 +08:00
38 changed files with 10071 additions and 18 deletions

6901
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

15
Cargo.toml Normal file
View File

@@ -0,0 +1,15 @@
[workspace]
members = ["crates/*", "src-tauri"]
resolver = "2"
[workspace.package]
edition = "2021"
rust-version = "1.85.0"
[workspace.dependencies]
sea-orm = { version = "^2.0.0-rc", features = ["sqlx-sqlite", "runtime-tokio-native-tls"] }
sea-orm-migration = { version = "^2.0.0-rc", features = ["runtime-tokio-native-tls", "sqlx-sqlite"] }
tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] }
chrono = { version = "0.4.43", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

View File

@@ -0,0 +1,18 @@
[package]
name = "migration"
version = "0.1.0"
edition = "2021"
rust-version = "1.85.0"
publish = false
[lib]
name = "migration"
path = "src/lib.rs"
[[bin]]
name = "migration-cli"
path = "src/main.rs"
[dependencies]
tokio = { workspace = true }
sea-orm-migration = { workspace = true }

View File

@@ -0,0 +1,41 @@
# Running Migrator CLI
- Generate a new migration file
```sh
cargo run -- generate MIGRATION_NAME
```
- Apply all pending migrations
```sh
cargo run
```
```sh
cargo run -- up
```
- Apply first 10 pending migrations
```sh
cargo run -- up -n 10
```
- Rollback last applied migrations
```sh
cargo run -- down
```
- Rollback last 10 applied migrations
```sh
cargo run -- down -n 10
```
- Drop all tables from the database, then reapply all migrations
```sh
cargo run -- fresh
```
- Rollback all applied migrations, then reapply all migrations
```sh
cargo run -- refresh
```
- Rollback all applied migrations
```sh
cargo run -- reset
```
- Check the status of all migrations
```sh
cargo run -- status
```

View File

@@ -0,0 +1,30 @@
pub use sea_orm_migration::prelude::*;
mod m20250214_000001_create_accounts;
mod m20250214_000002_create_settings_and_exchange_rates;
mod m20250214_000003_create_goals_and_tags;
mod m20250214_000004_create_scheduled_transactions;
mod m20250214_000005_create_transactions;
mod m20250214_000006_create_reconciliations_and_transfers;
mod m20250214_000007_create_transaction_tags;
mod m20250214_000008_create_goal_rules_and_progress;
mod m20250214_000009_create_scheduled_instances;
pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20250214_000001_create_accounts::Migration),
Box::new(m20250214_000002_create_settings_and_exchange_rates::Migration),
Box::new(m20250214_000003_create_goals_and_tags::Migration),
Box::new(m20250214_000004_create_scheduled_transactions::Migration),
Box::new(m20250214_000005_create_transactions::Migration),
Box::new(m20250214_000006_create_reconciliations_and_transfers::Migration),
Box::new(m20250214_000007_create_transaction_tags::Migration),
Box::new(m20250214_000008_create_goal_rules_and_progress::Migration),
Box::new(m20250214_000009_create_scheduled_instances::Migration),
]
}
}

View File

@@ -0,0 +1,65 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(Accounts::Table)
.if_not_exists()
.col(string(Accounts::Id).primary_key())
.col(string(Accounts::Name))
.col(string(Accounts::AccountType))
.col(string(Accounts::Currency))
.col(string(Accounts::InitialBalance))
.col(string(Accounts::CurrentBalance))
.col(string_null(Accounts::Color))
.col(string_null(Accounts::Icon))
.col(integer(Accounts::SortOrder))
.col(boolean(Accounts::IsActive))
.col(boolean(Accounts::IsArchived))
.col(boolean(Accounts::IncludeInNetWorth))
.col(boolean(Accounts::ShowInCombinedView))
.col(date_time(Accounts::CreatedAt))
.col(date_time(Accounts::UpdatedAt))
.col(integer(Accounts::Version))
.col(string_null(Accounts::DeviceId))
.col(boolean(Accounts::IsDeleted))
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Accounts::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Accounts {
Table,
Id,
Name,
AccountType,
Currency,
InitialBalance,
CurrentBalance,
Color,
Icon,
SortOrder,
IsActive,
IsArchived,
IncludeInNetWorth,
ShowInCombinedView,
CreatedAt,
UpdatedAt,
Version,
DeviceId,
IsDeleted,
}

View File

@@ -0,0 +1,72 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Create settings table
manager
.create_table(
Table::create()
.table(Settings::Table)
.if_not_exists()
.col(string(Settings::Key).primary_key())
.col(string_null(Settings::Value))
.col(date_time(Settings::UpdatedAt))
.to_owned(),
)
.await?;
// Create exchange_rates table
manager
.create_table(
Table::create()
.table(ExchangeRates::Table)
.if_not_exists()
.col(string(ExchangeRates::FromCurrency))
.col(string(ExchangeRates::ToCurrency))
.col(string(ExchangeRates::Date))
.col(string(ExchangeRates::Rate))
.col(string_null(ExchangeRates::Source))
.col(date_time_null(ExchangeRates::FetchedAt))
.primary_key(
Index::create()
.col(ExchangeRates::FromCurrency)
.col(ExchangeRates::ToCurrency)
.col(ExchangeRates::Date),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(ExchangeRates::Table).to_owned())
.await?;
manager
.drop_table(Table::drop().table(Settings::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Settings {
Table,
Key,
Value,
UpdatedAt,
}
#[derive(DeriveIden)]
enum ExchangeRates {
Table,
FromCurrency,
ToCurrency,
Date,
Rate,
Source,
FetchedAt,
}

View File

@@ -0,0 +1,132 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Create goals table
manager
.create_table(
Table::create()
.table(Goals::Table)
.if_not_exists()
.col(string(Goals::Id).primary_key())
.col(string(Goals::Name))
.col(string_null(Goals::Description))
.col(string(Goals::TargetAmount))
.col(string(Goals::CurrentAmount))
.col(string(Goals::Currency))
.col(string(Goals::GoalType))
.col(string_null(Goals::TargetDate))
.col(boolean(Goals::IsRecurring))
.col(string_null(Goals::RecurrencePeriod))
.col(string_null(Goals::LinkedAccountId))
.col(string_null(Goals::Color))
.col(string_null(Goals::Icon))
.col(boolean(Goals::IsActive))
.col(boolean(Goals::IsAchieved))
.col(date_time_null(Goals::AchievedAt))
.col(string_null(Goals::LastResetDate))
.col(date_time(Goals::CreatedAt))
.col(date_time(Goals::UpdatedAt))
.col(integer(Goals::Version))
.col(string_null(Goals::DeviceId))
.col(boolean(Goals::IsDeleted))
.foreign_key(
ForeignKey::create()
.name("fk_goals_account")
.from(Goals::Table, Goals::LinkedAccountId)
.to(Accounts::Table, Accounts::Id)
.on_delete(ForeignKeyAction::SetNull)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create tags table
manager
.create_table(
Table::create()
.table(Tags::Table)
.if_not_exists()
.col(string(Tags::Id).primary_key())
.col(string(Tags::Name))
.col(string(Tags::Color))
.col(string_null(Tags::Icon))
.col(string_null(Tags::BudgetAmount))
.col(string_null(Tags::BudgetPeriod))
.col(boolean(Tags::IsSystem))
.col(integer(Tags::SortOrder))
.col(date_time(Tags::CreatedAt))
.col(date_time(Tags::UpdatedAt))
.col(integer(Tags::Version))
.col(string_null(Tags::DeviceId))
.col(boolean(Tags::IsDeleted))
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Tags::Table).to_owned())
.await?;
manager
.drop_table(Table::drop().table(Goals::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Goals {
Table,
Id,
Name,
Description,
TargetAmount,
CurrentAmount,
Currency,
GoalType,
TargetDate,
IsRecurring,
RecurrencePeriod,
LinkedAccountId,
Color,
Icon,
IsActive,
IsAchieved,
AchievedAt,
LastResetDate,
CreatedAt,
UpdatedAt,
Version,
DeviceId,
IsDeleted,
}
#[derive(DeriveIden)]
enum Accounts {
Table,
Id,
}
#[derive(DeriveIden)]
enum Tags {
Table,
Id,
Name,
Color,
Icon,
BudgetAmount,
BudgetPeriod,
IsSystem,
SortOrder,
CreatedAt,
UpdatedAt,
Version,
DeviceId,
IsDeleted,
}

View File

@@ -0,0 +1,103 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(ScheduledTransactions::Table)
.if_not_exists()
.col(string(ScheduledTransactions::Id).primary_key())
.col(string(ScheduledTransactions::AccountId))
.col(string(ScheduledTransactions::ScheduleType))
.col(integer(ScheduledTransactions::Frequency))
.col(string_null(ScheduledTransactions::DaysOfWeek))
.col(integer_null(ScheduledTransactions::DayOfMonth))
.col(integer_null(ScheduledTransactions::MonthOfYear))
.col(string(ScheduledTransactions::ExecutionTime))
.col(string_null(ScheduledTransactions::Timezone))
.col(string(ScheduledTransactions::StartDate))
.col(string_null(ScheduledTransactions::EndDate))
.col(integer_null(ScheduledTransactions::OccurrenceCount))
.col(integer(ScheduledTransactions::CurrentOccurrence))
.col(string(ScheduledTransactions::TransactionType))
.col(string(ScheduledTransactions::GrossAmount))
.col(string(ScheduledTransactions::TaxAmount))
.col(string(ScheduledTransactions::NetAmount))
.col(string(ScheduledTransactions::Currency))
.col(string_null(ScheduledTransactions::Description))
.col(string_null(ScheduledTransactions::Merchant))
.col(string_null(ScheduledTransactions::Notes))
.col(string_null(ScheduledTransactions::TagIds))
.col(boolean(ScheduledTransactions::IsActive))
.col(string_null(ScheduledTransactions::LastGeneratedDate))
.col(date_time_null(ScheduledTransactions::NextExecutionDatetime))
.col(date_time(ScheduledTransactions::CreatedAt))
.col(date_time(ScheduledTransactions::UpdatedAt))
.col(integer(ScheduledTransactions::Version))
.col(string_null(ScheduledTransactions::DeviceId))
.col(boolean(ScheduledTransactions::IsDeleted))
.foreign_key(
ForeignKey::create()
.name("fk_scheduled_transactions_account")
.from(ScheduledTransactions::Table, ScheduledTransactions::AccountId)
.to(Accounts::Table, Accounts::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(ScheduledTransactions::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum ScheduledTransactions {
Table,
Id,
AccountId,
ScheduleType,
Frequency,
DaysOfWeek,
DayOfMonth,
MonthOfYear,
ExecutionTime,
Timezone,
StartDate,
EndDate,
OccurrenceCount,
CurrentOccurrence,
TransactionType,
GrossAmount,
TaxAmount,
NetAmount,
Currency,
Description,
Merchant,
Notes,
TagIds,
IsActive,
LastGeneratedDate,
NextExecutionDatetime,
CreatedAt,
UpdatedAt,
Version,
DeviceId,
IsDeleted,
}
#[derive(DeriveIden)]
enum Accounts {
Table,
Id,
}

View File

@@ -0,0 +1,109 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(Transactions::Table)
.if_not_exists()
.col(string(Transactions::Id).primary_key())
.col(string(Transactions::AccountId))
.col(string(Transactions::TransactionType))
.col(string(Transactions::GrossAmount))
.col(string(Transactions::TaxAmount))
.col(string(Transactions::NetAmount))
.col(string_null(Transactions::TaxRate))
.col(string(Transactions::Currency))
.col(string(Transactions::Description))
.col(string_null(Transactions::Merchant))
.col(string_null(Transactions::Notes))
.col(string_null(Transactions::ReceiptPaths))
.col(string_null(Transactions::ReceiptOcrData))
.col(string_null(Transactions::TransferId))
.col(string_null(Transactions::RelatedTransactionId))
.col(string_null(Transactions::ScheduleId))
.col(boolean(Transactions::IsScheduledInstance))
.col(boolean(Transactions::IsAutoInserted))
.col(boolean(Transactions::NeedsReview))
.col(string(Transactions::TransactionDate))
.col(date_time(Transactions::CreatedAt))
.col(date_time(Transactions::UpdatedAt))
.col(integer(Transactions::Version))
.col(string_null(Transactions::DeviceId))
.col(boolean(Transactions::IsDeleted))
.col(string(Transactions::SyncStatus))
.foreign_key(
ForeignKey::create()
.name("fk_transactions_account")
.from(Transactions::Table, Transactions::AccountId)
.to(Accounts::Table, Accounts::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("fk_transactions_schedule")
.from(Transactions::Table, Transactions::ScheduleId)
.to(ScheduledTransactions::Table, ScheduledTransactions::Id)
.on_delete(ForeignKeyAction::SetNull)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Transactions::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Transactions {
Table,
Id,
AccountId,
TransactionType,
GrossAmount,
TaxAmount,
NetAmount,
TaxRate,
Currency,
Description,
Merchant,
Notes,
ReceiptPaths,
ReceiptOcrData,
TransferId,
RelatedTransactionId,
ScheduleId,
IsScheduledInstance,
IsAutoInserted,
NeedsReview,
TransactionDate,
CreatedAt,
UpdatedAt,
Version,
DeviceId,
IsDeleted,
SyncStatus,
}
#[derive(DeriveIden)]
enum Accounts {
Table,
Id,
}
#[derive(DeriveIden)]
enum ScheduledTransactions {
Table,
Id,
}

View File

@@ -0,0 +1,118 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Create reconciliations table
manager
.create_table(
Table::create()
.table(Reconciliations::Table)
.if_not_exists()
.col(string(Reconciliations::Id).primary_key())
.col(string(Reconciliations::AccountId))
.col(string(Reconciliations::StatementDate))
.col(string(Reconciliations::StatementBalance))
.col(string(Reconciliations::AppBalance))
.col(string(Reconciliations::Difference))
.col(string(Reconciliations::Status))
.col(string_null(Reconciliations::Notes))
.col(date_time(Reconciliations::CreatedAt))
.col(date_time_null(Reconciliations::ResolvedAt))
.col(integer(Reconciliations::Version))
.col(string_null(Reconciliations::DeviceId))
.foreign_key(
ForeignKey::create()
.name("fk_reconciliations_account")
.from(Reconciliations::Table, Reconciliations::AccountId)
.to(Accounts::Table, Accounts::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create transfers table
manager
.create_table(
Table::create()
.table(Transfers::Table)
.if_not_exists()
.col(string(Transfers::Id).primary_key())
.col(string(Transfers::FromAccountId))
.col(string(Transfers::ToAccountId))
.col(string_null(Transfers::FromTransactionId))
.col(string_null(Transfers::ToTransactionId))
.col(string(Transfers::FromAmount))
.col(string(Transfers::ToAmount))
.col(string_null(Transfers::ExchangeRate))
.col(string_null(Transfers::ExchangeRateSource))
.col(string(Transfers::Fees))
.col(string_null(Transfers::Description))
.col(string(Transfers::TransferDate))
.col(date_time(Transfers::CreatedAt))
.col(integer(Transfers::Version))
.col(string_null(Transfers::DeviceId))
.col(boolean(Transfers::IsDeleted))
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Transfers::Table).to_owned())
.await?;
manager
.drop_table(Table::drop().table(Reconciliations::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Reconciliations {
Table,
Id,
AccountId,
StatementDate,
StatementBalance,
AppBalance,
Difference,
Status,
Notes,
CreatedAt,
ResolvedAt,
Version,
DeviceId,
}
#[derive(DeriveIden)]
enum Transfers {
Table,
Id,
FromAccountId,
ToAccountId,
FromTransactionId,
ToTransactionId,
FromAmount,
ToAmount,
ExchangeRate,
ExchangeRateSource,
Fees,
Description,
TransferDate,
CreatedAt,
Version,
DeviceId,
IsDeleted,
}
#[derive(DeriveIden)]
enum Accounts {
Table,
Id,
}

View File

@@ -0,0 +1,66 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(TransactionTags::Table)
.if_not_exists()
.col(string(TransactionTags::TransactionId))
.col(string(TransactionTags::TagId))
.primary_key(
Index::create()
.col(TransactionTags::TransactionId)
.col(TransactionTags::TagId),
)
.foreign_key(
ForeignKey::create()
.name("fk_transaction_tags_transaction")
.from(TransactionTags::Table, TransactionTags::TransactionId)
.to(Transactions::Table, Transactions::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("fk_transaction_tags_tag")
.from(TransactionTags::Table, TransactionTags::TagId)
.to(Tags::Table, Tags::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(TransactionTags::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum TransactionTags {
Table,
TransactionId,
TagId,
}
#[derive(DeriveIden)]
enum Transactions {
Table,
Id,
}
#[derive(DeriveIden)]
enum Tags {
Table,
Id,
}

View File

@@ -0,0 +1,124 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Create goal_rules table
manager
.create_table(
Table::create()
.table(GoalRules::Table)
.if_not_exists()
.col(string(GoalRules::Id).primary_key())
.col(string(GoalRules::GoalId))
.col(string(GoalRules::TagIds))
.col(string(GoalRules::ContributionType))
.col(string_null(GoalRules::Percentage))
.col(string_null(GoalRules::FixedAmount))
.col(string_null(GoalRules::MaxContributionPerTransaction))
.col(string_null(GoalRules::MonthlyCap))
.col(boolean(GoalRules::IsActive))
.col(date_time(GoalRules::CreatedAt))
.col(date_time(GoalRules::UpdatedAt))
.col(integer(GoalRules::Version))
.col(string_null(GoalRules::DeviceId))
.col(boolean(GoalRules::IsDeleted))
.foreign_key(
ForeignKey::create()
.name("fk_goal_rules_goal")
.from(GoalRules::Table, GoalRules::GoalId)
.to(Goals::Table, Goals::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create goal_progress table
manager
.create_table(
Table::create()
.table(GoalProgress::Table)
.if_not_exists()
.col(string(GoalProgress::Id).primary_key())
.col(string(GoalProgress::GoalId))
.col(string(GoalProgress::Amount))
.col(string_null(GoalProgress::TransactionId))
.col(string_null(GoalProgress::Notes))
.col(date_time(GoalProgress::RecordedAt))
.foreign_key(
ForeignKey::create()
.name("fk_goal_progress_goal")
.from(GoalProgress::Table, GoalProgress::GoalId)
.to(Goals::Table, Goals::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("fk_goal_progress_transaction")
.from(GoalProgress::Table, GoalProgress::TransactionId)
.to(Transactions::Table, Transactions::Id)
.on_delete(ForeignKeyAction::SetNull)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(GoalProgress::Table).to_owned())
.await?;
manager
.drop_table(Table::drop().table(GoalRules::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum GoalRules {
Table,
Id,
GoalId,
TagIds,
ContributionType,
Percentage,
FixedAmount,
MaxContributionPerTransaction,
MonthlyCap,
IsActive,
CreatedAt,
UpdatedAt,
Version,
DeviceId,
IsDeleted,
}
#[derive(DeriveIden)]
enum GoalProgress {
Table,
Id,
GoalId,
Amount,
TransactionId,
Notes,
RecordedAt,
}
#[derive(DeriveIden)]
enum Goals {
Table,
Id,
}
#[derive(DeriveIden)]
enum Transactions {
Table,
Id,
}

View File

@@ -0,0 +1,75 @@
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(ScheduledInstances::Table)
.if_not_exists()
.col(string(ScheduledInstances::Id).primary_key())
.col(string(ScheduledInstances::ScheduleId))
.col(string_null(ScheduledInstances::TransactionId))
.col(string(ScheduledInstances::DueDate))
.col(boolean(ScheduledInstances::IsGenerated))
.col(boolean(ScheduledInstances::IsSkipped))
.col(date_time_null(ScheduledInstances::GeneratedAt))
.col(boolean(ScheduledInstances::Notified))
.col(date_time(ScheduledInstances::CreatedAt))
.foreign_key(
ForeignKey::create()
.name("fk_scheduled_instances_schedule")
.from(ScheduledInstances::Table, ScheduledInstances::ScheduleId)
.to(ScheduledTransactions::Table, ScheduledTransactions::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("fk_scheduled_instances_transaction")
.from(ScheduledInstances::Table, ScheduledInstances::TransactionId)
.to(Transactions::Table, Transactions::Id)
.on_delete(ForeignKeyAction::SetNull)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(ScheduledInstances::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum ScheduledInstances {
Table,
Id,
ScheduleId,
TransactionId,
DueDate,
IsGenerated,
IsSkipped,
GeneratedAt,
Notified,
CreatedAt,
}
#[derive(DeriveIden)]
enum ScheduledTransactions {
Table,
Id,
}
#[derive(DeriveIden)]
enum Transactions {
Table,
Id,
}

View File

@@ -0,0 +1,6 @@
use sea_orm_migration::prelude::*;
#[tokio::main]
async fn main() {
cli::run_cli(migration::Migrator).await;
}

View File

@@ -13,7 +13,10 @@ db-migrate:
-u sqlite://temp.db \
-o ../src-tauri/src/db/entities \
--with-serde both \
--date-time-crate chrono
--date-time-crate chrono \
--with-prelude all-allow-unused-imports \
--model-extra-attributes 'allow(dead_code)' \
--enum-extra-attributes 'allow(dead_code)'
rm crates/temp.db

1387
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,25 +4,30 @@ version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
rust-version = "1.85.0"
[lib]
# The `_lib` suffix may seem redundant but it is necessary
# to make the lib name unique and wouldn't conflict with the bin name.
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
name = "finwise_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[[bin]]
name = "finwise"
path = "src/main.rs"
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde = { workspace = true }
serde_json = { workspace = true }
sea-orm = { workspace = true }
chrono = { workspace = true }
migration = { path = "../crates/migration" }
thiserror = "1"
rust_decimal = "1"
uuid = { version = "1", features = ["v4"] }
[profile.dev]
incremental = true

View File

@@ -0,0 +1,47 @@
use std::path::PathBuf;
use sea_orm::{Database, DatabaseConnection, DbErr};
use tauri::{AppHandle, Manager};
use super::migrations;
const DATABASE_PATH: &str = "finance.db";
pub(super) async fn establish_connection(
app_handle: &AppHandle,
) -> Result<DatabaseConnection, DbErr> {
let app_dir = app_handle
.path()
.app_data_dir()
.expect("Failed to get app data directory");
// Create directory if it doesn't exist
std::fs::create_dir_all(&app_dir).expect("Failed to create app data directory");
let db_path = app_dir.join(DATABASE_PATH);
let url = format!("sqlite://{}?mode=rwc", db_path.display());
println!("Connecting to database at: {}", db_path.display());
let db = Database::connect(&url).await?;
// Enable foreign keys and set pragmas
sea_orm::ConnectionTrait::execute_unprepared(
&db,
"PRAGMA foreign_keys = ON; PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL;",
)
.await?;
// Run migrations
migrations::run_migrations(&db).await?;
Ok(db)
}
pub(super) fn get_database_path(app_handle: &AppHandle) -> PathBuf {
app_handle
.path()
.app_data_dir()
.expect("Failed to get app data directory")
.join(DATABASE_PATH)
}

View File

@@ -0,0 +1,67 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "accounts")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub name: String,
pub account_type: String,
pub currency: String,
pub initial_balance: String,
pub current_balance: String,
pub color: Option<String>,
pub icon: Option<String>,
pub sort_order: i64,
pub is_active: bool,
pub is_archived: bool,
pub include_in_net_worth: bool,
pub show_in_combined_view: bool,
pub created_at: DateTime,
pub updated_at: DateTime,
pub version: i64,
pub device_id: Option<String>,
pub is_deleted: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::goals::Entity")]
Goals,
#[sea_orm(has_many = "super::reconciliations::Entity")]
Reconciliations,
#[sea_orm(has_many = "super::scheduled_transactions::Entity")]
ScheduledTransactions,
#[sea_orm(has_many = "super::transactions::Entity")]
Transactions,
}
impl Related<super::goals::Entity> for Entity {
fn to() -> RelationDef {
Relation::Goals.def()
}
}
impl Related<super::reconciliations::Entity> for Entity {
fn to() -> RelationDef {
Relation::Reconciliations.def()
}
}
impl Related<super::scheduled_transactions::Entity> for Entity {
fn to() -> RelationDef {
Relation::ScheduledTransactions.def()
}
}
impl Related<super::transactions::Entity> for Entity {
fn to() -> RelationDef {
Relation::Transactions.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,24 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "exchange_rates")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub from_currency: String,
#[sea_orm(primary_key, auto_increment = false)]
pub to_currency: String,
#[sea_orm(primary_key, auto_increment = false)]
pub date: String,
pub rate: String,
pub source: Option<String>,
pub fetched_at: Option<DateTime>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,51 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "goal_progress")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub goal_id: String,
pub amount: String,
pub transaction_id: Option<String>,
pub notes: Option<String>,
pub recorded_at: DateTime,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::goals::Entity",
from = "Column::GoalId",
to = "super::goals::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Goals,
#[sea_orm(
belongs_to = "super::transactions::Entity",
from = "Column::TransactionId",
to = "super::transactions::Column::Id",
on_update = "Cascade",
on_delete = "SetNull"
)]
Transactions,
}
impl Related<super::goals::Entity> for Entity {
fn to() -> RelationDef {
Relation::Goals.def()
}
}
impl Related<super::transactions::Entity> for Entity {
fn to() -> RelationDef {
Relation::Transactions.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,45 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "goal_rules")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub goal_id: String,
pub tag_ids: String,
pub contribution_type: String,
pub percentage: Option<String>,
pub fixed_amount: Option<String>,
pub max_contribution_per_transaction: Option<String>,
pub monthly_cap: Option<String>,
pub is_active: bool,
pub created_at: DateTime,
pub updated_at: DateTime,
pub version: i64,
pub device_id: Option<String>,
pub is_deleted: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::goals::Entity",
from = "Column::GoalId",
to = "super::goals::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Goals,
}
impl Related<super::goals::Entity> for Entity {
fn to() -> RelationDef {
Relation::Goals.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,69 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "goals")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub name: String,
pub description: Option<String>,
pub target_amount: String,
pub current_amount: String,
pub currency: String,
pub goal_type: String,
pub target_date: Option<String>,
pub is_recurring: bool,
pub recurrence_period: Option<String>,
pub linked_account_id: Option<String>,
pub color: Option<String>,
pub icon: Option<String>,
pub is_active: bool,
pub is_achieved: bool,
pub achieved_at: Option<DateTime>,
pub last_reset_date: Option<String>,
pub created_at: DateTime,
pub updated_at: DateTime,
pub version: i64,
pub device_id: Option<String>,
pub is_deleted: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::accounts::Entity",
from = "Column::LinkedAccountId",
to = "super::accounts::Column::Id",
on_update = "Cascade",
on_delete = "SetNull"
)]
Accounts,
#[sea_orm(has_many = "super::goal_progress::Entity")]
GoalProgress,
#[sea_orm(has_many = "super::goal_rules::Entity")]
GoalRules,
}
impl Related<super::accounts::Entity> for Entity {
fn to() -> RelationDef {
Relation::Accounts.def()
}
}
impl Related<super::goal_progress::Entity> for Entity {
fn to() -> RelationDef {
Relation::GoalProgress.def()
}
}
impl Related<super::goal_rules::Entity> for Entity {
fn to() -> RelationDef {
Relation::GoalRules.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,17 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
pub mod prelude;
pub mod accounts;
pub mod exchange_rates;
pub mod goal_progress;
pub mod goal_rules;
pub mod goals;
pub mod reconciliations;
pub mod scheduled_instances;
pub mod scheduled_transactions;
pub mod settings;
pub mod tags;
pub mod transaction_tags;
pub mod transactions;
pub mod transfers;

View File

@@ -0,0 +1,17 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
#![allow(unused_imports)]
pub use super::accounts::Entity as Accounts;
pub use super::exchange_rates::Entity as ExchangeRates;
pub use super::goal_progress::Entity as GoalProgress;
pub use super::goal_rules::Entity as GoalRules;
pub use super::goals::Entity as Goals;
pub use super::reconciliations::Entity as Reconciliations;
pub use super::scheduled_instances::Entity as ScheduledInstances;
pub use super::scheduled_transactions::Entity as ScheduledTransactions;
pub use super::settings::Entity as Settings;
pub use super::tags::Entity as Tags;
pub use super::transaction_tags::Entity as TransactionTags;
pub use super::transactions::Entity as Transactions;
pub use super::transfers::Entity as Transfers;

View File

@@ -0,0 +1,43 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "reconciliations")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub account_id: String,
pub statement_date: String,
pub statement_balance: String,
pub app_balance: String,
pub difference: String,
pub status: String,
pub notes: Option<String>,
pub created_at: DateTime,
pub resolved_at: Option<DateTime>,
pub version: i64,
pub device_id: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::accounts::Entity",
from = "Column::AccountId",
to = "super::accounts::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Accounts,
}
impl Related<super::accounts::Entity> for Entity {
fn to() -> RelationDef {
Relation::Accounts.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,54 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "scheduled_instances")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub schedule_id: String,
pub transaction_id: Option<String>,
pub due_date: String,
pub is_generated: bool,
pub is_skipped: bool,
pub generated_at: Option<DateTime>,
pub notified: bool,
pub created_at: DateTime,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::scheduled_transactions::Entity",
from = "Column::ScheduleId",
to = "super::scheduled_transactions::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
ScheduledTransactions,
#[sea_orm(
belongs_to = "super::transactions::Entity",
from = "Column::TransactionId",
to = "super::transactions::Column::Id",
on_update = "Cascade",
on_delete = "SetNull"
)]
Transactions,
}
impl Related<super::scheduled_transactions::Entity> for Entity {
fn to() -> RelationDef {
Relation::ScheduledTransactions.def()
}
}
impl Related<super::transactions::Entity> for Entity {
fn to() -> RelationDef {
Relation::Transactions.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,77 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "scheduled_transactions")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub account_id: String,
pub schedule_type: String,
pub frequency: i64,
pub days_of_week: Option<String>,
pub day_of_month: Option<i64>,
pub month_of_year: Option<i64>,
pub execution_time: String,
pub timezone: Option<String>,
pub start_date: String,
pub end_date: Option<String>,
pub occurrence_count: Option<i64>,
pub current_occurrence: i64,
pub transaction_type: String,
pub gross_amount: String,
pub tax_amount: String,
pub net_amount: String,
pub currency: String,
pub description: Option<String>,
pub merchant: Option<String>,
pub notes: Option<String>,
pub tag_ids: Option<String>,
pub is_active: bool,
pub last_generated_date: Option<String>,
pub next_execution_datetime: Option<DateTime>,
pub created_at: DateTime,
pub updated_at: DateTime,
pub version: i64,
pub device_id: Option<String>,
pub is_deleted: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::accounts::Entity",
from = "Column::AccountId",
to = "super::accounts::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Accounts,
#[sea_orm(has_many = "super::scheduled_instances::Entity")]
ScheduledInstances,
#[sea_orm(has_many = "super::transactions::Entity")]
Transactions,
}
impl Related<super::accounts::Entity> for Entity {
fn to() -> RelationDef {
Relation::Accounts.def()
}
}
impl Related<super::scheduled_instances::Entity> for Entity {
fn to() -> RelationDef {
Relation::ScheduledInstances.def()
}
}
impl Related<super::transactions::Entity> for Entity {
fn to() -> RelationDef {
Relation::Transactions.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,19 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "settings")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub key: String,
pub value: Option<String>,
pub updated_at: DateTime,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,47 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "tags")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub name: String,
pub color: String,
pub icon: Option<String>,
pub budget_amount: Option<String>,
pub budget_period: Option<String>,
pub is_system: bool,
pub sort_order: i64,
pub created_at: DateTime,
pub updated_at: DateTime,
pub version: i64,
pub device_id: Option<String>,
pub is_deleted: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::transaction_tags::Entity")]
TransactionTags,
}
impl Related<super::transaction_tags::Entity> for Entity {
fn to() -> RelationDef {
Relation::TransactionTags.def()
}
}
impl Related<super::transactions::Entity> for Entity {
fn to() -> RelationDef {
super::transaction_tags::Relation::Transactions.def()
}
fn via() -> Option<RelationDef> {
Some(super::transaction_tags::Relation::Tags.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,48 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "transaction_tags")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub transaction_id: String,
#[sea_orm(primary_key, auto_increment = false)]
pub tag_id: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::tags::Entity",
from = "Column::TagId",
to = "super::tags::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Tags,
#[sea_orm(
belongs_to = "super::transactions::Entity",
from = "Column::TransactionId",
to = "super::transactions::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Transactions,
}
impl Related<super::tags::Entity> for Entity {
fn to() -> RelationDef {
Relation::Tags.def()
}
}
impl Related<super::transactions::Entity> for Entity {
fn to() -> RelationDef {
Relation::Transactions.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,104 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "transactions")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub account_id: String,
pub transaction_type: String,
pub gross_amount: String,
pub tax_amount: String,
pub net_amount: String,
pub tax_rate: Option<String>,
pub currency: String,
pub description: String,
pub merchant: Option<String>,
pub notes: Option<String>,
pub receipt_paths: Option<String>,
pub receipt_ocr_data: Option<String>,
pub transfer_id: Option<String>,
pub related_transaction_id: Option<String>,
pub schedule_id: Option<String>,
pub is_scheduled_instance: bool,
pub is_auto_inserted: bool,
pub needs_review: bool,
pub transaction_date: String,
pub created_at: DateTime,
pub updated_at: DateTime,
pub version: i64,
pub device_id: Option<String>,
pub is_deleted: bool,
pub sync_status: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::accounts::Entity",
from = "Column::AccountId",
to = "super::accounts::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Accounts,
#[sea_orm(has_many = "super::goal_progress::Entity")]
GoalProgress,
#[sea_orm(has_many = "super::scheduled_instances::Entity")]
ScheduledInstances,
#[sea_orm(
belongs_to = "super::scheduled_transactions::Entity",
from = "Column::ScheduleId",
to = "super::scheduled_transactions::Column::Id",
on_update = "Cascade",
on_delete = "SetNull"
)]
ScheduledTransactions,
#[sea_orm(has_many = "super::transaction_tags::Entity")]
TransactionTags,
}
impl Related<super::accounts::Entity> for Entity {
fn to() -> RelationDef {
Relation::Accounts.def()
}
}
impl Related<super::goal_progress::Entity> for Entity {
fn to() -> RelationDef {
Relation::GoalProgress.def()
}
}
impl Related<super::scheduled_instances::Entity> for Entity {
fn to() -> RelationDef {
Relation::ScheduledInstances.def()
}
}
impl Related<super::scheduled_transactions::Entity> for Entity {
fn to() -> RelationDef {
Relation::ScheduledTransactions.def()
}
}
impl Related<super::transaction_tags::Entity> for Entity {
fn to() -> RelationDef {
Relation::TransactionTags.def()
}
}
impl Related<super::tags::Entity> for Entity {
fn to() -> RelationDef {
super::transaction_tags::Relation::Tags.def()
}
fn via() -> Option<RelationDef> {
Some(super::transaction_tags::Relation::Transactions.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,32 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "transfers")]
#[allow(dead_code)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub from_account_id: String,
pub to_account_id: String,
pub from_transaction_id: Option<String>,
pub to_transaction_id: Option<String>,
pub from_amount: String,
pub to_amount: String,
pub exchange_rate: Option<String>,
pub exchange_rate_source: Option<String>,
pub fees: String,
pub description: Option<String>,
pub transfer_date: String,
pub created_at: DateTime,
pub version: i64,
pub device_id: Option<String>,
pub is_deleted: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,7 @@
use migration::{Migrator, MigratorTrait};
use sea_orm::DatabaseConnection;
pub(super) async fn run_migrations(conn: &DatabaseConnection) -> Result<(), sea_orm::DbErr> {
Migrator::up(conn, None).await?;
Ok(())
}

5
src-tauri/src/db/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
pub mod entities;
pub mod service;
mod connection;
mod migrations;

View File

@@ -0,0 +1,26 @@
use sea_orm::{DatabaseConnection, DbErr};
use super::connection;
pub struct DbService {
connection: DatabaseConnection,
}
impl DbService {
pub async fn new(app_handle: &tauri::AppHandle) -> Result<Self, DbErr> {
let connection = connection::establish_connection(app_handle).await?;
Ok(Self { connection })
}
pub fn get_connection(&self) -> &DatabaseConnection {
&self.connection
}
pub fn get_connection_mut(&mut self) -> &mut DatabaseConnection {
&mut self.connection
}
pub async fn run_migrations(&self) -> Result<(), DbErr> {
crate::db::migrations::run_migrations(&self.connection).await
}
}

View File

@@ -1,3 +1,5 @@
mod db;
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
#[tauri::command]
fn greet(name: &str) -> String {