summaryrefslogtreecommitdiff
path: root/backend/core
diff options
context:
space:
mode:
authorJoe Carstairs <me@joeac.net>2024-10-16 18:22:55 +0100
committerJoe Carstairs <me@joeac.net>2024-10-17 06:55:02 +0100
commit7adaace61a4fe983eba5ded3aaaf624ce6fb9014 (patch)
tree6e0697f9dbb6e249c60af5a92479a86316c55221 /backend/core
parent27b95c9186bd45edd4c982a7cc89cf69cf2c5d46 (diff)
Refactors some backend stuff to core lib
Diffstat (limited to 'backend/core')
-rw-r--r--backend/core/2024-08-31-084439_initial_setup/down.sql6
-rw-r--r--backend/core/2024-08-31-084439_initial_setup/up.sql70
-rw-r--r--backend/core/Cargo.lock628
-rw-r--r--backend/core/Cargo.toml10
-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
17 files changed, 1073 insertions, 0 deletions
diff --git a/backend/core/2024-08-31-084439_initial_setup/down.sql b/backend/core/2024-08-31-084439_initial_setup/down.sql
new file mode 100644
index 0000000..8cf4b1e
--- /dev/null
+++ b/backend/core/2024-08-31-084439_initial_setup/down.sql
@@ -0,0 +1,6 @@
+DROP TABLE accounts;
+DROP TABLE budget_updates;
+DROP TABLE categories;
+DROP TABLE transaction_categorisations;
+DROP TABLE category_transfers;
+DROP TABLE transactions;
diff --git a/backend/core/2024-08-31-084439_initial_setup/up.sql b/backend/core/2024-08-31-084439_initial_setup/up.sql
new file mode 100644
index 0000000..3584ae7
--- /dev/null
+++ b/backend/core/2024-08-31-084439_initial_setup/up.sql
@@ -0,0 +1,70 @@
+PRAGMA foreign_keys = ON;
+
+CREATE TABLE accounts(
+ id INTEGER NOT NULL PRIMARY KEY,
+ name TEXT NOT NULL,
+ opening_balance INTEGER NOT NULL,
+ opening_date TEXT NOT NULL
+);
+
+CREATE TABLE budget_updates(
+ id INTEGER NOT NULL PRIMARY KEY,
+ category_id INTEGER NOT NULL,
+ date TEXT NOT NULL,
+ new_budget INTEGER NOT NULL,
+ new_period INTEGER NOT NULL,
+ FOREIGN KEY (category_id)
+ REFERENCES categories (id)
+ ON UPDATE CASCADE
+ ON DELETE RESTRICT
+);
+
+CREATE TABLE categories(
+ id INTEGER NOT NULL PRIMARY KEY,
+ name TEXT NOT NULL
+);
+
+CREATE TABLE category_transfers(
+ id INTEGER NOT NULL PRIMARY KEY,
+ description TEXT NOT NULL,
+ quantity INTEGER NOT NULL,
+ from_category_id INTEGER NOT NULL,
+ to_category_id INTEGER NOT NULL,
+ FOREIGN KEY (from_category_id)
+ REFERENCES categories (id)
+ ON UPDATE CASCADE
+ ON DELETE RESTRICT,
+ FOREIGN KEY (to_category_id)
+ REFERENCES categories (id)
+ ON UPDATE CASCADE
+ ON DELETE RESTRICT
+);
+
+CREATE TABLE transactions(
+ id INTEGER NOT NULL PRIMARY KEY,
+ description TEXT NOT NULL,
+ payee TEXT NOT NULL,
+ quantity INTEGER NOT NULL,
+ date TEXT NOT NULL,
+ account_id INTEGER NOT NULL,
+ FOREIGN KEY (account_id)
+ REFERENCES accounts (id)
+ ON UPDATE CASCADE
+ ON DELETE RESTRICT
+);
+
+CREATE TABLE transaction_categorisations(
+ id INTEGER NOT NULL PRIMARY KEY,
+ description TEXT NOT NULL,
+ quantity INTEGER NOT NULL,
+ transaction_id INTEGER NOT NULL,
+ category_id INTEGER NOT NULL,
+ FOREIGN KEY (transaction_id)
+ REFERENCES transactions (id)
+ ON UPDATE CASCADE
+ ON DELETE RESTRICT,
+ FOREIGN KEY (category_id)
+ REFERENCES categories (id)
+ ON UPDATE CASCADE
+ ON DELETE RESTRICT
+);
diff --git a/backend/core/Cargo.lock b/backend/core/Cargo.lock
new file mode 100644
index 0000000..0a4e0fd
--- /dev/null
+++ b/backend/core/Cargo.lock
@@ -0,0 +1,628 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "budgeting_app_core"
+version = "0.1.0"
+dependencies = [
+ "chrono",
+ "diesel",
+ "diesel_migrations",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "cc"
+version = "1.1.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-targets",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "darling"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "diesel"
+version = "2.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e"
+dependencies = [
+ "diesel_derives",
+ "libsqlite3-sys",
+ "time",
+]
+
+[[package]]
+name = "diesel_derives"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4"
+dependencies = [
+ "diesel_table_macro_syntax",
+ "dsl_auto_type",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "diesel_migrations"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6"
+dependencies = [
+ "diesel",
+ "migrations_internals",
+ "migrations_macros",
+]
+
+[[package]]
+name = "diesel_table_macro_syntax"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25"
+dependencies = [
+ "syn",
+]
+
+[[package]]
+name = "dsl_auto_type"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607"
+dependencies = [
+ "darling",
+ "either",
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "indexmap"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "js-sys"
+version = "0.3.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.159"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "migrations_internals"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff"
+dependencies = [
+ "serde",
+ "toml",
+]
+
+[[package]]
+name = "migrations_macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd"
+dependencies = [
+ "migrations_internals",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "time"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winnow"
+version = "0.6.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
+dependencies = [
+ "memchr",
+]
diff --git a/backend/core/Cargo.toml b/backend/core/Cargo.toml
new file mode 100644
index 0000000..09b1ebf
--- /dev/null
+++ b/backend/core/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "budgeting_app_core"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+chrono = { version = "0.4.38", features = ["serde"] }
+diesel = { version = "2.2.0", features = ["sqlite"] }
+diesel_migrations = "2.2.0"
+serde = { version = "1.0.209", features = ["derive"] }
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,
+);