diff --git a/.idea/finance-consumer.iml b/.idea/finance-consumer.iml
index a8b96a4..5859576 100644
--- a/.idea/finance-consumer.iml
+++ b/.idea/finance-consumer.iml
@@ -9,5 +9,6 @@
+
\ No newline at end of file
diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml
new file mode 100644
index 0000000..13898e9
--- /dev/null
+++ b/.idea/jsLibraryMappings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 0ea32f7..a60b665 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -11,15 +11,18 @@
-
-
-
+
+
+
+
-
+
+
+
@@ -54,12 +57,15 @@
+
+
+
+
+
@@ -145,7 +156,8 @@
-
+
+
@@ -171,7 +183,15 @@
1735137853863
-
+
+
+ 1735406260589
+
+
+
+ 1735406260589
+
+
diff --git a/bookkeeper/Cargo.lock b/bookkeeper/Cargo.lock
index 4091fb3..f04c3f5 100644
--- a/bookkeeper/Cargo.lock
+++ b/bookkeeper/Cargo.lock
@@ -433,6 +433,8 @@ dependencies = [
"rust_decimal",
"sea-orm",
"sea-orm-rocket",
+ "serde",
+ "serde_json",
]
[[package]]
@@ -3029,9 +3031,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.133"
+version = "1.0.134"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
+checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
dependencies = [
"itoa",
"memchr",
diff --git a/bookkeeper/Cargo.toml b/bookkeeper/Cargo.toml
index 65392e3..63dd2a9 100644
--- a/bookkeeper/Cargo.toml
+++ b/bookkeeper/Cargo.toml
@@ -11,6 +11,8 @@ migration = { path = "migration" }
entity ={ path = "entity"}
rust_decimal = "1.36.0"
sea-orm-rocket = "0.5.4"
+serde_json = "1.0.134"
+serde = { version = "1.0.216", features = ["derive"] }
[workspace]
members = [".", "entity", "migration"]
diff --git a/bookkeeper/entity/src/transaction.rs b/bookkeeper/entity/src/transaction.rs
index 0136735..a0ba407 100644
--- a/bookkeeper/entity/src/transaction.rs
+++ b/bookkeeper/entity/src/transaction.rs
@@ -15,14 +15,11 @@ pub struct Model {
pub buy: Decimal,
pub volume: i32,
pub net_buy: Decimal,
- pub sell_date: Option,
- pub sell: Option,
- pub net_sell: Option,
- pub sold_volume: Option,
#[sea_orm(column_type = "JsonBinary", nullable)]
pub split_pieces: Option,
pub gain: Option,
pub net_gain: Option,
+ pub is_done: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
diff --git a/bookkeeper/migration/src/m20220101_000001_create_table.rs b/bookkeeper/migration/src/m20220101_000001_create_table.rs
index 9ef14b6..2ac5935 100644
--- a/bookkeeper/migration/src/m20220101_000001_create_table.rs
+++ b/bookkeeper/migration/src/m20220101_000001_create_table.rs
@@ -30,13 +30,10 @@ impl MigrationTrait for Migration {
.col(decimal(Transaction::Buy))
.col(integer(Transaction::Volume))
.col(decimal(Transaction::NetBuy))
- .col(date_null(Transaction::SellDate))
- .col(decimal_null(Transaction::Sell))
- .col(decimal_null(Transaction::NetSell))
- .col(integer_null(Transaction::SoldVolume))
.col(json_binary_null(Transaction::SplitPieces))
.col(decimal_null(Transaction::Gain))
.col(decimal_null(Transaction::NetGain))
+ .col(boolean(Transaction::IsDone))
.to_owned(),
)
.await?;
@@ -79,11 +76,8 @@ enum Transaction {
Buy,
Volume,
NetBuy,
- Sell,
- SellDate,
- NetSell,
- SoldVolume,
SplitPieces,
Gain,
NetGain,
+ IsDone,
}
\ No newline at end of file
diff --git a/bookkeeper/src/main.rs b/bookkeeper/src/main.rs
index 09e4424..b529d78 100644
--- a/bookkeeper/src/main.rs
+++ b/bookkeeper/src/main.rs
@@ -19,6 +19,7 @@ use sea_orm::{ActiveModelTrait, ColumnTrait, ConnectOptions, DatabaseConnection,
use sea_orm_rocket::{rocket::figment::Figment, Config, Connection, Database};
use std::str::FromStr;
use std::time::Duration;
+use serde::{Deserialize, Serialize};
#[derive(sea_orm_rocket::Database, Debug)]
#[database("bookkeeper")]
@@ -69,32 +70,88 @@ async fn index(conn: Connection<'_, Db>) -> Template {
}
#[derive(FromForm)]
-struct TransBuy<'r> {
+struct TransModel<'r> {
code: usize,
name: &'r str,
buy: &'r str,
- volume: usize,
+ #[field(validate = range(100..))]
+ volume: i32,
net_buy: &'r str,
date: &'r str,
+ split_pieces: Vec>,
}
-#[post("/buy", data = "")]
-async fn buy(trans_buy: Form>, conn: Connection<'_, Db>) -> Flash {
- let record = transaction::ActiveModel {
- code: Set(trans_buy.code as i32),
- name: Set(trans_buy.name.to_string()),
- buy: Set(Decimal::from_str(trans_buy.buy).unwrap()),
- volume: Set(trans_buy.volume as i32),
- net_buy: Set(Decimal::from_str(trans_buy.net_buy).unwrap()),
- date: Set(NaiveDate::parse_from_str(trans_buy.date, "%Y-%m-%d").unwrap()),
- ..Default::default()
- };
+#[derive(FromForm, Serialize)]
+struct SplitPieceForm<'r> {
+ date: &'r str,
+ sell: &'r str,
+ net_sell: &'r str,
+ #[field(validate = range(100..))]
+ volume: i32,
+}
+
+#[derive(Deserialize, Serialize)]
+struct SplitPiece{
+ date: NaiveDate,
+ sell: Decimal,
+ net_sell: Decimal,
+ volume: i32,
+}
+
+#[post("/add", data = "")]
+async fn add(trans: Form>, conn: Connection<'_, Db>) -> Flash {
+ let record = translate(trans);
record.insert(conn.into_inner()).await.unwrap();
Flash::success(Redirect::to("/"), "OK!")
}
+/// translate from form to active model of transaction
+fn translate(trans: Form) -> transaction::ActiveModel {
+ let mut record = transaction::ActiveModel {
+ code: Set(trans.code as i32),
+ name: Set(trans.name.to_string()),
+ buy: Set(Decimal::from_str(trans.buy).unwrap()),
+ volume: Set(trans.volume),
+ net_buy: Set(Decimal::from_str(trans.net_buy).unwrap()),
+ date: Set(NaiveDate::parse_from_str(trans.date, "%Y-%m-%d").unwrap()),
+ is_done: Set(false),
+ ..Default::default()
+ };
+
+ if trans.split_pieces.len() > 0 {
+ let mut sp: Vec = Vec::new();
+ for split_piece in trans.split_pieces.iter() {
+ sp.push(SplitPiece{
+ date: NaiveDate::parse_from_str(split_piece.date, "%Y-%m-%d").unwrap(),
+ sell: Decimal::from_str(split_piece.sell).unwrap(),
+ net_sell: Decimal::from_str(split_piece.net_sell).unwrap(),
+ volume: split_piece.volume,
+ })
+ }
+
+ let total: i32 = sp.iter().fold(0, |acc, e| { acc + e.volume });
+ if total == trans.volume {
+ record.is_done = Set(true);
+ let sell_int = sp.iter().fold(Decimal::new(0, 4), |acc, e| {
+ acc + e.sell * Decimal::from(e.volume)
+ });
+ let buy = Decimal::from_str(trans.buy).unwrap() * Decimal::from(total);
+ record.gain = Set(Option::from((sell_int - buy) / buy * Decimal::from(100)));
+
+ let net_sell_int = sp.iter().fold(Decimal::from(0), |acc, e| { acc + e.net_sell });
+ let net_buy = Decimal::from_str(trans.net_buy).unwrap();
+ record.net_gain = Set(Option::from((net_sell_int - net_buy) / net_buy * Decimal::from(100)));
+ }
+
+ let v: serde_json::value::Value = serde_json::to_value(sp).unwrap();
+ record.split_pieces = Set(Option::from(v));
+ }
+
+ record
+}
+
async fn run_migrations(rocket: Rocket) -> fairing::Result {
let conn = &Db::fetch(&rocket).unwrap().conn;
let _ = migration::Migrator::up(conn, None).await;
@@ -108,5 +165,5 @@ async fn rocket() -> _ {
.attach(Db::init())
.attach(AdHoc::try_on_ignite("Migrations", run_migrations))
.attach(Template::fairing())
- .mount("/", routes![index, buy])
+ .mount("/", routes![index, add])
}
diff --git a/bookkeeper/templates/base.html.j2 b/bookkeeper/templates/base.html.j2
index 160c968..6ea0d0f 100644
--- a/bookkeeper/templates/base.html.j2
+++ b/bookkeeper/templates/base.html.j2
@@ -7,74 +7,6 @@
-
-{% block content %}{% endblock content %}
+{% block content %}{% endblock content %}