summaryrefslogtreecommitdiff
path: root/backend/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'backend/core/src')
-rw-r--r--backend/core/src/lib.rs6
-rw-r--r--backend/core/src/models/account.rs15
-rw-r--r--backend/core/src/models/budget.rs44
-rw-r--r--backend/core/src/models/budget_update.rs17
-rw-r--r--backend/core/src/models/category.rs9
-rw-r--r--backend/core/src/models/category_balance.rs7
-rw-r--r--backend/core/src/models/category_budget.rs9
-rw-r--r--backend/core/src/models/category_transfer.rs11
-rw-r--r--backend/core/src/models/date_time_utc.rs131
-rw-r--r--backend/core/src/models/mod.rs10
-rw-r--r--backend/core/src/models/transaction.rs17
-rw-r--r--backend/core/src/models/transaction_categorisation.rs11
-rw-r--r--backend/core/src/schema.rs72
13 files changed, 359 insertions, 0 deletions
diff --git a/backend/core/src/lib.rs b/backend/core/src/lib.rs
new file mode 100644
index 0000000..7a56560
--- /dev/null
+++ b/backend/core/src/lib.rs
@@ -0,0 +1,6 @@
+use diesel_migrations::{embed_migrations, EmbeddedMigrations};
+
+pub mod models;
+pub mod schema;
+
+pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
diff --git a/backend/core/src/models/account.rs b/backend/core/src/models/account.rs
new file mode 100644
index 0000000..c10bd5f
--- /dev/null
+++ b/backend/core/src/models/account.rs
@@ -0,0 +1,15 @@
+use diesel::prelude::*;
+use serde::{Deserialize, Serialize};
+
+use super::date_time_utc::DateTimeUtc;
+
+#[derive(
+ Insertable, Queryable, Identifiable, Selectable, Debug, PartialEq, Clone, Serialize, Deserialize,
+)]
+#[diesel(table_name = crate::schema::accounts)]
+pub struct Account {
+ pub id: i32,
+ pub name: String,
+ pub opening_balance: i32,
+ pub opening_date: DateTimeUtc,
+}
diff --git a/backend/core/src/models/budget.rs b/backend/core/src/models/budget.rs
new file mode 100644
index 0000000..cc18f2e
--- /dev/null
+++ b/backend/core/src/models/budget.rs
@@ -0,0 +1,44 @@
+use serde::Serialize;
+
+use super::budget_update::BudgetUpdate;
+
+#[derive(Debug, Eq, PartialEq, Serialize)]
+pub struct Budget {
+ pub quantity: i32,
+ pub period: i32,
+}
+
+impl From<&BudgetUpdate> for Budget {
+ fn from(budget_update: &BudgetUpdate) -> Self {
+ Self {
+ quantity: budget_update.new_budget,
+ period: budget_update.new_period,
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use chrono::Utc;
+
+ use crate::models::date_time_utc::DateTimeUtc;
+
+ use super::*;
+
+ #[test]
+ fn when_from_budget_update_then_returns_budget() {
+ let quantity = 2;
+ let period = 3;
+ let budget_update = BudgetUpdate {
+ id: 0,
+ category_id: 1,
+ date: DateTimeUtc::new(Utc::now()),
+ new_budget: quantity,
+ new_period: period,
+ };
+
+ let budget = Budget::from(&budget_update);
+
+ assert_eq!(budget, Budget { quantity, period });
+ }
+}
diff --git a/backend/core/src/models/budget_update.rs b/backend/core/src/models/budget_update.rs
new file mode 100644
index 0000000..c63e957
--- /dev/null
+++ b/backend/core/src/models/budget_update.rs
@@ -0,0 +1,17 @@
+use diesel::prelude::*;
+use serde::Serialize;
+
+use crate::models::category::Category;
+
+use super::date_time_utc::DateTimeUtc;
+
+#[derive(Clone, Serialize, Queryable, Identifiable, Selectable, Associations, Debug, PartialEq)]
+#[diesel(table_name = crate::schema::budget_updates)]
+#[diesel(belongs_to(Category))]
+pub struct BudgetUpdate {
+ pub id: i32,
+ pub category_id: i32,
+ pub date: DateTimeUtc,
+ pub new_budget: i32,
+ pub new_period: i32,
+}
diff --git a/backend/core/src/models/category.rs b/backend/core/src/models/category.rs
new file mode 100644
index 0000000..5cb2609
--- /dev/null
+++ b/backend/core/src/models/category.rs
@@ -0,0 +1,9 @@
+use diesel::prelude::*;
+use serde::{Deserialize, Serialize};
+
+#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq, Serialize, Deserialize)]
+#[diesel(table_name = crate::schema::categories)]
+pub struct Category {
+ pub id: i32,
+ pub name: String,
+}
diff --git a/backend/core/src/models/category_balance.rs b/backend/core/src/models/category_balance.rs
new file mode 100644
index 0000000..796b40b
--- /dev/null
+++ b/backend/core/src/models/category_balance.rs
@@ -0,0 +1,7 @@
+use serde::Serialize;
+
+#[derive(Serialize)]
+pub struct CategoryBalance {
+ pub category_id: i32,
+ pub balance: i32,
+}
diff --git a/backend/core/src/models/category_budget.rs b/backend/core/src/models/category_budget.rs
new file mode 100644
index 0000000..9796261
--- /dev/null
+++ b/backend/core/src/models/category_budget.rs
@@ -0,0 +1,9 @@
+use serde::Serialize;
+
+use super::budget::Budget;
+
+#[derive(Serialize)]
+pub struct CategoryBudget {
+ pub category_id: i32,
+ pub budget: Option<Budget>,
+}
diff --git a/backend/core/src/models/category_transfer.rs b/backend/core/src/models/category_transfer.rs
new file mode 100644
index 0000000..6cbda0e
--- /dev/null
+++ b/backend/core/src/models/category_transfer.rs
@@ -0,0 +1,11 @@
+use diesel::prelude::*;
+
+#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)]
+#[diesel(table_name = crate::schema::category_transfers)]
+pub struct CategoryTransfer {
+ pub id: i32,
+ pub description: String,
+ pub quantity: i32,
+ pub from_category_id: i32,
+ pub to_category_id: i32,
+}
diff --git a/backend/core/src/models/date_time_utc.rs b/backend/core/src/models/date_time_utc.rs
new file mode 100644
index 0000000..03d5802
--- /dev/null
+++ b/backend/core/src/models/date_time_utc.rs
@@ -0,0 +1,131 @@
+use chrono::{DateTime, Datelike, Utc};
+use diesel::{
+ backend::Backend,
+ deserialize::{self, FromSql, FromSqlRow},
+ expression::AsExpression,
+ serialize::{self, ToSql},
+ sql_types::Text,
+ sqlite::Sqlite,
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(
+ Eq,
+ PartialEq,
+ Ord,
+ PartialOrd,
+ Serialize,
+ Deserialize,
+ Copy,
+ Clone,
+ Debug,
+ AsExpression,
+ FromSqlRow,
+)]
+#[diesel(sql_type=Text)]
+pub struct DateTimeUtc(DateTime<Utc>);
+
+impl DateTimeUtc {
+ pub fn day_diff(&self, other: &DateTimeUtc) -> u32 {
+ self.0
+ .num_days_from_ce()
+ .abs_diff(other.0.num_days_from_ce())
+ }
+
+ pub fn to_day_str(&self) -> String {
+ self.0
+ .to_rfc3339()
+ .split_at_checked(10)
+ .map(|(day_str, _)| String::from(day_str))
+ .unwrap_or_else(|| String::from(""))
+ }
+
+ pub fn new(date_time_utc: DateTime<Utc>) -> Self {
+ Self(date_time_utc)
+ }
+
+ pub fn now() -> Self {
+ Self(Utc::now())
+ }
+}
+
+impl ToSql<Text, Sqlite> for DateTimeUtc {
+ fn to_sql<'b>(
+ &'b self,
+ out: &mut diesel::serialize::Output<'b, '_, Sqlite>,
+ ) -> serialize::Result {
+ out.set_value(self.to_day_str());
+ Ok(diesel::serialize::IsNull::No)
+ }
+}
+
+impl FromSql<Text, Sqlite> for DateTimeUtc {
+ fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<DateTimeUtc> {
+ let str: String = FromSql::<Text, Sqlite>::from_sql(bytes)?;
+ let date_time = str.as_str().parse::<DateTime<Utc>>()?;
+ Ok(DateTimeUtc(date_time))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use chrono::TimeZone;
+
+ use super::*;
+
+ #[test]
+ fn given_midnight_when_day_diff_next_midnight_less_1s_then_return_0() {
+ let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc());
+ let end = DateTimeUtc::new(
+ Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59)
+ .unwrap()
+ .to_utc(),
+ );
+
+ let day_diff = start.day_diff(&end);
+ let neg_day_diff = end.day_diff(&start);
+
+ assert_eq!(day_diff, 0);
+ assert_eq!(day_diff, neg_day_diff);
+ }
+
+ #[test]
+ fn given_midnight_when_day_diff_next_midnight_then_return_1() {
+ let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc());
+ let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc());
+
+ let day_diff = start.day_diff(&end);
+ let neg_day_diff = end.day_diff(&start);
+
+ assert_eq!(day_diff, 1);
+ assert_eq!(day_diff, neg_day_diff);
+ }
+
+ #[test]
+ fn given_midnight_less_1s_when_day_diff_midnight_then_return_1() {
+ let start = DateTimeUtc::new(
+ Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59)
+ .unwrap()
+ .to_utc(),
+ );
+ let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc());
+
+ let day_diff = start.day_diff(&end);
+ let neg_day_diff = end.day_diff(&start);
+
+ assert_eq!(day_diff, 1);
+ assert_eq!(day_diff, neg_day_diff);
+ }
+
+ #[test]
+ fn given_23_nov_1970_when_to_day_str_then_return_iso_day_str() {
+ let date_time_utc = DateTimeUtc::new(
+ Utc.with_ymd_and_hms(1970, 11, 23, 0, 0, 0)
+ .unwrap()
+ .to_utc(),
+ );
+ let day_str = date_time_utc.to_day_str();
+
+ assert_eq!(day_str, "1970-11-23");
+ }
+}
diff --git a/backend/core/src/models/mod.rs b/backend/core/src/models/mod.rs
new file mode 100644
index 0000000..b790fcb
--- /dev/null
+++ b/backend/core/src/models/mod.rs
@@ -0,0 +1,10 @@
+pub mod account;
+pub mod budget;
+pub mod budget_update;
+pub mod category;
+pub mod category_balance;
+pub mod category_budget;
+pub mod category_transfer;
+pub mod date_time_utc;
+pub mod transaction;
+pub mod transaction_categorisation;
diff --git a/backend/core/src/models/transaction.rs b/backend/core/src/models/transaction.rs
new file mode 100644
index 0000000..36f33f8
--- /dev/null
+++ b/backend/core/src/models/transaction.rs
@@ -0,0 +1,17 @@
+use diesel::prelude::*;
+
+use crate::models::account::Account;
+
+use super::date_time_utc::DateTimeUtc;
+
+#[derive(Queryable, Identifiable, Selectable, Associations, Debug, PartialEq)]
+#[diesel(table_name = crate::schema::transactions)]
+#[diesel(belongs_to(Account))]
+pub struct Transaction {
+ pub id: i32,
+ pub description: String,
+ pub payee: String,
+ pub quantity: i32,
+ pub date: DateTimeUtc,
+ pub account_id: i32,
+}
diff --git a/backend/core/src/models/transaction_categorisation.rs b/backend/core/src/models/transaction_categorisation.rs
new file mode 100644
index 0000000..60c2b98
--- /dev/null
+++ b/backend/core/src/models/transaction_categorisation.rs
@@ -0,0 +1,11 @@
+use diesel::prelude::*;
+
+#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)]
+#[diesel(table_name = crate::schema::transaction_categorisations)]
+pub struct TransactionCategorisation {
+ pub id: i32,
+ pub description: String,
+ pub quantity: i32,
+ pub transaction_id: i32,
+ pub category_id: i32,
+}
diff --git a/backend/core/src/schema.rs b/backend/core/src/schema.rs
new file mode 100644
index 0000000..372f8f3
--- /dev/null
+++ b/backend/core/src/schema.rs
@@ -0,0 +1,72 @@
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+ accounts (id) {
+ id -> Integer,
+ name -> Text,
+ opening_balance -> Integer,
+ opening_date -> Text,
+ }
+}
+
+diesel::table! {
+ budget_updates (id) {
+ id -> Integer,
+ category_id -> Integer,
+ date -> Text,
+ new_budget -> Integer,
+ new_period -> Integer,
+ }
+}
+
+diesel::table! {
+ categories (id) {
+ id -> Integer,
+ name -> Text,
+ }
+}
+
+diesel::table! {
+ category_transfers (id) {
+ id -> Integer,
+ description -> Text,
+ quantity -> Integer,
+ from_category_id -> Integer,
+ to_category_id -> Integer,
+ }
+}
+
+diesel::table! {
+ transaction_categorisations (id) {
+ id -> Integer,
+ description -> Text,
+ quantity -> Integer,
+ transaction_id -> Integer,
+ category_id -> Integer,
+ }
+}
+
+diesel::table! {
+ transactions (id) {
+ id -> Integer,
+ description -> Text,
+ payee -> Text,
+ quantity -> Integer,
+ date -> Text,
+ account_id -> Integer,
+ }
+}
+
+diesel::joinable!(budget_updates -> categories (category_id));
+diesel::joinable!(transaction_categorisations -> categories (category_id));
+diesel::joinable!(transaction_categorisations -> transactions (transaction_id));
+diesel::joinable!(transactions -> accounts (account_id));
+
+diesel::allow_tables_to_appear_in_same_query!(
+ accounts,
+ budget_updates,
+ categories,
+ category_transfers,
+ transaction_categorisations,
+ transactions,
+);