添加总计
This commit is contained in:
parent
0ee56aad24
commit
4b052d8b2a
@ -10,13 +10,15 @@
|
|||||||
<cargoProject FILE="$PROJECT_DIR$/bookkeeper/Cargo.toml" />
|
<cargoProject FILE="$PROJECT_DIR$/bookkeeper/Cargo.toml" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="76b3b902-7a5c-4bcd-9c1b-8241d748fb44" name="更改" comment="更新版本号和部署脚本">
|
<list default="true" id="76b3b902-7a5c-4bcd-9c1b-8241d748fb44" name="更改" comment="清仓筛选">
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/bookkeeper/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/Cargo.lock" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/bookkeeper/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/Cargo.lock" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/bookkeeper/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/Cargo.toml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/bookkeeper/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/Cargo.toml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/bookkeeper/migration/src/m20220101_000001_create_table.rs" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/migration/src/m20220101_000001_create_table.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/bookkeeper/migration/src/m20241230_160326_gain_evolution.rs" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/migration/src/m20241230_160326_gain_evolution.rs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/bookkeeper/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/src/main.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/bookkeeper/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/src/main.rs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/bookkeeper/templates/index.html.j2" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/templates/index.html.j2" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/bookkeeper/templates/index.html.j2" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/templates/index.html.j2" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/bookkeeper/templates/nav.html.j2" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/templates/nav.html.j2" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/bookkeeper/templates/tx.html.j2" beforeDir="false" afterPath="$PROJECT_DIR$/bookkeeper/templates/tx.html.j2" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@ -83,7 +85,7 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="RunManager" selected="Cargo.Run bookkeeper">
|
<component name="RunManager" selected="Cargo.Run bookkeeper">
|
||||||
<configuration name="Run bookkeeper" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
<configuration name="Run bookkeeper" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
<option name="buildProfileId" value="release" />
|
<option name="buildProfileId" value="dev" />
|
||||||
<option name="command" value="run --package bookkeeper --bin bookkeeper" />
|
<option name="command" value="run --package bookkeeper --bin bookkeeper" />
|
||||||
<option name="workingDirectory" value="file://$PROJECT_DIR$/bookkeeper" />
|
<option name="workingDirectory" value="file://$PROJECT_DIR$/bookkeeper" />
|
||||||
<envs />
|
<envs />
|
||||||
@ -154,7 +156,9 @@
|
|||||||
<workItem from="1735470376833" duration="16014000" />
|
<workItem from="1735470376833" duration="16014000" />
|
||||||
<workItem from="1735553493290" duration="16207000" />
|
<workItem from="1735553493290" duration="16207000" />
|
||||||
<workItem from="1736238929025" duration="646000" />
|
<workItem from="1736238929025" duration="646000" />
|
||||||
<workItem from="1736239592084" duration="1828000" />
|
<workItem from="1736239592084" duration="2278000" />
|
||||||
|
<workItem from="1737809478583" duration="76000" />
|
||||||
|
<workItem from="1739197188908" duration="1079000" />
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="Add bookkeeper">
|
<task id="LOCAL-00001" summary="Add bookkeeper">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@ -252,7 +256,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1736241063964</updated>
|
<updated>1736241063964</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="13" />
|
<task id="LOCAL-00013" summary="清仓筛选">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1736241771673</created>
|
||||||
|
<option name="number" value="00013" />
|
||||||
|
<option name="presentableId" value="LOCAL-00013" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1736241771673</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="14" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
@ -282,7 +294,8 @@
|
|||||||
<MESSAGE value="Refine gain algorithm and some UI update" />
|
<MESSAGE value="Refine gain algorithm and some UI update" />
|
||||||
<MESSAGE value="Filter by name" />
|
<MESSAGE value="Filter by name" />
|
||||||
<MESSAGE value="更新版本号和部署脚本" />
|
<MESSAGE value="更新版本号和部署脚本" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="更新版本号和部署脚本" />
|
<MESSAGE value="清仓筛选" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="清仓筛选" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XSLT-Support.FileAssociations.UIState">
|
<component name="XSLT-Support.FileAssociations.UIState">
|
||||||
<expand />
|
<expand />
|
||||||
|
|||||||
2
bookkeeper/Cargo.lock
generated
2
bookkeeper/Cargo.lock
generated
@ -423,7 +423,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bookkeeper"
|
name = "bookkeeper"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"entity",
|
"entity",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bookkeeper"
|
name = "bookkeeper"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -6,36 +6,37 @@ pub struct Migration;
|
|||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl MigrationTrait for Migration {
|
impl MigrationTrait for Migration {
|
||||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
manager.create_table(
|
.create_table(
|
||||||
Table::create()
|
Table::create()
|
||||||
.table(User::Table)
|
.table(User::Table)
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(pk_auto(User::Id))
|
.col(pk_auto(User::Id))
|
||||||
.col(string(User::Name))
|
.col(string(User::Name))
|
||||||
.col(string(User::Password))
|
.col(string(User::Password))
|
||||||
.col(string(User::SessionToken))
|
.col(string(User::SessionToken))
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
manager.create_table(
|
manager
|
||||||
Table::create()
|
.create_table(
|
||||||
.table(Transaction::Table)
|
Table::create()
|
||||||
.if_not_exists()
|
.table(Transaction::Table)
|
||||||
.col(pk_auto(Transaction::Id))
|
.if_not_exists()
|
||||||
.col(integer(Transaction::Code))
|
.col(pk_auto(Transaction::Id))
|
||||||
.col(string(Transaction::Name))
|
.col(integer(Transaction::Code))
|
||||||
.col(date(Transaction::Date))
|
.col(string(Transaction::Name))
|
||||||
.col(decimal(Transaction::Buy))
|
.col(date(Transaction::Date))
|
||||||
.col(integer(Transaction::Volume))
|
.col(decimal(Transaction::Buy))
|
||||||
.col(decimal(Transaction::NetBuy))
|
.col(integer(Transaction::Volume))
|
||||||
.col(json_binary_null(Transaction::SplitPieces))
|
.col(decimal(Transaction::NetBuy))
|
||||||
.col(decimal_null(Transaction::Gain))
|
.col(json_binary_null(Transaction::SplitPieces))
|
||||||
.col(decimal_null(Transaction::NetGain))
|
.col(decimal_null(Transaction::Gain))
|
||||||
.col(boolean(Transaction::IsDone))
|
.col(decimal_null(Transaction::NetGain))
|
||||||
.to_owned(),
|
.col(boolean(Transaction::IsDone))
|
||||||
)
|
.to_owned(),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -80,4 +81,4 @@ enum Transaction {
|
|||||||
Gain,
|
Gain,
|
||||||
NetGain,
|
NetGain,
|
||||||
IsDone,
|
IsDone,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
use std::str::FromStr;
|
|
||||||
use sea_orm_migration::prelude::*;
|
use sea_orm_migration::prelude::*;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::ActiveValue::Set;
|
use sea_orm::ActiveValue::Set;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq,)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
#[sea_orm(table_name = "transaction")]
|
#[sea_orm(table_name = "transaction")]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(primary_key)]
|
#[sea_orm(primary_key)]
|
||||||
@ -45,11 +45,14 @@ impl MigrationTrait for Migration {
|
|||||||
let mut total_net_sell = Decimal::from_str("0.0").unwrap();
|
let mut total_net_sell = Decimal::from_str("0.0").unwrap();
|
||||||
for piece in pieces {
|
for piece in pieces {
|
||||||
let p = piece.as_object().unwrap();
|
let p = piece.as_object().unwrap();
|
||||||
total_sell += Decimal::from_str(p.get("sell").unwrap().as_str().unwrap()).unwrap() *
|
total_sell += Decimal::from_str(p.get("sell").unwrap().as_str().unwrap()).unwrap()
|
||||||
Decimal::from(p.get("volume").unwrap().as_i64().unwrap());
|
* Decimal::from(p.get("volume").unwrap().as_i64().unwrap());
|
||||||
total_net_sell += Decimal::from_str(p.get("net_sell").unwrap().as_str().unwrap()).unwrap();
|
total_net_sell +=
|
||||||
|
Decimal::from_str(p.get("net_sell").unwrap().as_str().unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
r.gain = Set(Some(total_sell - r.buy.clone().unwrap() * Decimal::from(r.volume.clone().unwrap())));
|
r.gain = Set(Some(
|
||||||
|
total_sell - r.buy.clone().unwrap() * Decimal::from(r.volume.clone().unwrap()),
|
||||||
|
));
|
||||||
r.net_gain = Set(Some(total_net_sell - r.net_buy.clone().unwrap()));
|
r.net_gain = Set(Some(total_net_sell - r.net_buy.clone().unwrap()));
|
||||||
r.update(db).await?;
|
r.update(db).await?;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,8 @@ use entity::transaction::Entity as TE;
|
|||||||
use migration::MigratorTrait;
|
use migration::MigratorTrait;
|
||||||
use rocket::fairing::{self, AdHoc};
|
use rocket::fairing::{self, AdHoc};
|
||||||
use rocket::form::Form;
|
use rocket::form::Form;
|
||||||
|
use rocket::fs::FileServer;
|
||||||
|
use rocket::http::Status;
|
||||||
use rocket::response::{Flash, Redirect};
|
use rocket::response::{Flash, Redirect};
|
||||||
use rocket::{Build, Rocket};
|
use rocket::{Build, Rocket};
|
||||||
use rocket_dyn_templates::{context, Engines, Template};
|
use rocket_dyn_templates::{context, Engines, Template};
|
||||||
@ -17,11 +19,9 @@ use sea_orm::query::*;
|
|||||||
use sea_orm::ActiveValue::Set;
|
use sea_orm::ActiveValue::Set;
|
||||||
use sea_orm::{ActiveModelTrait, ColumnTrait, ConnectOptions, DatabaseConnection, EntityTrait};
|
use sea_orm::{ActiveModelTrait, ColumnTrait, ConnectOptions, DatabaseConnection, EntityTrait};
|
||||||
use sea_orm_rocket::{rocket::figment::Figment, Config, Connection, Database};
|
use sea_orm_rocket::{rocket::figment::Figment, Config, Connection, Database};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use rocket::http::Status;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use rocket::fs::FileServer;
|
|
||||||
|
|
||||||
#[derive(sea_orm_rocket::Database, Debug)]
|
#[derive(sea_orm_rocket::Database, Debug)]
|
||||||
#[database("bookkeeper")]
|
#[database("bookkeeper")]
|
||||||
@ -58,21 +58,27 @@ impl sea_orm_rocket::Pool for SeaOrmPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/?<last>&<name>&<is_done>")]
|
#[get("/?<last>&<name>&<is_done>")]
|
||||||
async fn index(conn: Connection<'_, Db>, last: Option<u32>, name: Option<&str>, is_done: Option<bool>) -> Template {
|
async fn index(
|
||||||
|
conn: Connection<'_, Db>,
|
||||||
|
last: Option<u32>,
|
||||||
|
name: Option<&str>,
|
||||||
|
is_done: Option<bool>,
|
||||||
|
) -> Template {
|
||||||
let mut partial = TE::find();
|
let mut partial = TE::find();
|
||||||
|
|
||||||
if let Some(last) = last {
|
if let Some(last) = last {
|
||||||
partial = partial.filter(transaction::Column::Date.gte(Local::now() - TimeDelta::days(last as i64)));
|
partial = partial
|
||||||
|
.filter(transaction::Column::Date.gte(Local::now() - TimeDelta::days(last as i64)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
partial = partial.filter(transaction::Column::Name.eq(name));
|
partial = partial.filter(transaction::Column::Name.eq(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(done) = is_done {
|
if let Some(done) = is_done {
|
||||||
partial = partial.filter(transaction::Column::IsDone.eq(done));
|
partial = partial.filter(transaction::Column::IsDone.eq(done));
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows: Vec<transaction::Model> = partial
|
let rows: Vec<transaction::Model> = partial
|
||||||
.order_by_desc(transaction::Column::Date)
|
.order_by_desc(transaction::Column::Date)
|
||||||
.order_by_desc(transaction::Column::Id)
|
.order_by_desc(transaction::Column::Id)
|
||||||
@ -81,11 +87,37 @@ async fn index(conn: Connection<'_, Db>, last: Option<u32>, name: Option<&str>,
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
let mut bought = Decimal::from(0);
|
||||||
|
let mut net_bought = Decimal::from(0);
|
||||||
|
let mut gain = Decimal::from(0);
|
||||||
|
let mut net_gain = Decimal::from(0);
|
||||||
|
|
||||||
|
for r in &rows {
|
||||||
|
if let Some(g) = r.gain {
|
||||||
|
bought += r.buy * Decimal::from(r.volume);
|
||||||
|
gain += g;
|
||||||
|
}
|
||||||
|
if let Some(ng) = r.net_gain {
|
||||||
|
net_bought += r.net_buy;
|
||||||
|
net_gain += ng;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Template::render("index", context! {
|
let pct = gain / bought;
|
||||||
rows: rows,
|
let net_pct = net_gain / net_bought;
|
||||||
version: version,
|
|
||||||
})
|
Template::render(
|
||||||
|
"index",
|
||||||
|
context! {
|
||||||
|
rows: rows,
|
||||||
|
version: version,
|
||||||
|
gain: gain,
|
||||||
|
net_gain: net_gain,
|
||||||
|
pct: pct,
|
||||||
|
net_pct: net_pct,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
#[derive(FromForm, Debug)]
|
||||||
@ -110,7 +142,7 @@ struct SplitPieceForm<'r> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
struct SplitPiece{
|
struct SplitPiece {
|
||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
sell: Decimal,
|
sell: Decimal,
|
||||||
net_sell: Decimal,
|
net_sell: Decimal,
|
||||||
@ -119,35 +151,37 @@ struct SplitPiece{
|
|||||||
|
|
||||||
#[get("/tx/<id>")]
|
#[get("/tx/<id>")]
|
||||||
async fn get_tx(conn: Connection<'_, Db>, id: i32) -> Result<Template, Status> {
|
async fn get_tx(conn: Connection<'_, Db>, id: i32) -> Result<Template, Status> {
|
||||||
let row = TE::find_by_id(id)
|
let row = TE::find_by_id(id).one(conn.into_inner()).await.unwrap();
|
||||||
.one(conn.into_inner())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
match row {
|
match row {
|
||||||
None => { Err(Status::NotFound) },
|
None => Err(Status::NotFound),
|
||||||
Some(row) => {
|
Some(row) => Ok(Template::render(
|
||||||
Ok(Template::render("tx", context! {
|
"tx",
|
||||||
|
context! {
|
||||||
r: row,
|
r: row,
|
||||||
target: "/tx/".to_owned() + id.to_string().as_str(),
|
target: "/tx/".to_owned() + id.to_string().as_str(),
|
||||||
}))
|
},
|
||||||
}
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/tx/<id>", data = "<data>")]
|
#[post("/tx/<id>", data = "<data>")]
|
||||||
async fn post_tx(data: Form<TransModel<'_>>, conn: Connection<'_, Db>, id: i32) -> Result<Redirect, Status> {
|
async fn post_tx(
|
||||||
|
data: Form<TransModel<'_>>,
|
||||||
|
conn: Connection<'_, Db>,
|
||||||
|
id: i32,
|
||||||
|
) -> Result<Redirect, Status> {
|
||||||
let mut record = translate(data);
|
let mut record = translate(data);
|
||||||
record.id = Set(id);
|
record.id = Set(id);
|
||||||
match record.update(conn.into_inner()).await {
|
match record.update(conn.into_inner()).await {
|
||||||
Err(_) => { Err(Status::NotFound) },
|
Err(_) => Err(Status::NotFound),
|
||||||
Ok(_) => { Ok(Redirect::to("/")) },
|
Ok(_) => Ok(Redirect::to("/")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/tx/<id>")]
|
#[delete("/tx/<id>")]
|
||||||
async fn delete_tx(conn: Connection<'_, Db>, id: i32) -> Result<Redirect, Status> {
|
async fn delete_tx(conn: Connection<'_, Db>, id: i32) -> Result<Redirect, Status> {
|
||||||
match TE::delete_by_id(id).exec(conn.into_inner()).await {
|
match TE::delete_by_id(id).exec(conn.into_inner()).await {
|
||||||
Err(_) => { Err(Status::InternalServerError) },
|
Err(_) => Err(Status::InternalServerError),
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
if res.rows_affected == 1 {
|
if res.rows_affected == 1 {
|
||||||
Ok(Redirect::to("/"))
|
Ok(Redirect::to("/"))
|
||||||
@ -160,10 +194,13 @@ async fn delete_tx(conn: Connection<'_, Db>, id: i32) -> Result<Redirect, Status
|
|||||||
|
|
||||||
#[get("/add")]
|
#[get("/add")]
|
||||||
async fn get_add() -> Template {
|
async fn get_add() -> Template {
|
||||||
Template::render("tx", context! {
|
Template::render(
|
||||||
r: (),
|
"tx",
|
||||||
target: "/add",
|
context! {
|
||||||
})
|
r: (),
|
||||||
|
target: "/add",
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/add", data = "<trans>")]
|
#[post("/add", data = "<trans>")]
|
||||||
@ -191,7 +228,7 @@ fn translate(trans: Form<TransModel>) -> transaction::ActiveModel {
|
|||||||
if let Some(split_pieces) = &trans.split_pieces {
|
if let Some(split_pieces) = &trans.split_pieces {
|
||||||
let mut sp: Vec<SplitPiece> = Vec::new();
|
let mut sp: Vec<SplitPiece> = Vec::new();
|
||||||
for split_piece in split_pieces.iter() {
|
for split_piece in split_pieces.iter() {
|
||||||
sp.push(SplitPiece{
|
sp.push(SplitPiece {
|
||||||
date: NaiveDate::parse_from_str(split_piece.date, "%Y-%m-%d").unwrap(),
|
date: NaiveDate::parse_from_str(split_piece.date, "%Y-%m-%d").unwrap(),
|
||||||
sell: Decimal::from_str(split_piece.sell).unwrap(),
|
sell: Decimal::from_str(split_piece.sell).unwrap(),
|
||||||
net_sell: Decimal::from_str(split_piece.net_sell).unwrap(),
|
net_sell: Decimal::from_str(split_piece.net_sell).unwrap(),
|
||||||
@ -199,7 +236,7 @@ fn translate(trans: Form<TransModel>) -> transaction::ActiveModel {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let total: i32 = sp.iter().fold(0, |acc, e| { acc + e.volume });
|
let total: i32 = sp.iter().fold(0, |acc, e| acc + e.volume);
|
||||||
if total == trans.volume {
|
if total == trans.volume {
|
||||||
record.is_done = Set(true);
|
record.is_done = Set(true);
|
||||||
let sell_int = sp.iter().fold(Decimal::new(0, 4), |acc, e| {
|
let sell_int = sp.iter().fold(Decimal::new(0, 4), |acc, e| {
|
||||||
@ -208,7 +245,7 @@ fn translate(trans: Form<TransModel>) -> transaction::ActiveModel {
|
|||||||
let buy = Decimal::from_str(trans.buy).unwrap() * Decimal::from(total);
|
let buy = Decimal::from_str(trans.buy).unwrap() * Decimal::from(total);
|
||||||
record.gain = Set(Some(sell_int - buy));
|
record.gain = Set(Some(sell_int - buy));
|
||||||
|
|
||||||
let net_sell_int = sp.iter().fold(Decimal::from(0), |acc, e| { acc + e.net_sell });
|
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();
|
let net_buy = Decimal::from_str(trans.net_buy).unwrap();
|
||||||
record.net_gain = Set(Some(net_sell_int - net_buy));
|
record.net_gain = Set(Some(net_sell_int - net_buy));
|
||||||
}
|
}
|
||||||
@ -232,14 +269,19 @@ async fn rocket() -> _ {
|
|||||||
.attach(Db::init())
|
.attach(Db::init())
|
||||||
.attach(AdHoc::try_on_ignite("Migrations", run_migrations))
|
.attach(AdHoc::try_on_ignite("Migrations", run_migrations))
|
||||||
.attach(Template::custom(|engines: &mut Engines| {
|
.attach(Template::custom(|engines: &mut Engines| {
|
||||||
engines.minijinja.add_filter("padLeft", |value: String, length: usize| -> String {
|
engines
|
||||||
if value.len() < length {
|
.minijinja
|
||||||
"0".repeat(length - value.len()) + value.as_str()
|
.add_filter("padLeft", |value: String, length: usize| -> String {
|
||||||
} else {
|
if value.len() < length {
|
||||||
value
|
"0".repeat(length - value.len()) + value.as_str()
|
||||||
}
|
} else {
|
||||||
})
|
value
|
||||||
|
}
|
||||||
|
})
|
||||||
}))
|
}))
|
||||||
.mount("/", routes![index, add, get_add, get_tx, post_tx, delete_tx])
|
.mount(
|
||||||
|
"/",
|
||||||
|
routes![index, add, get_add, get_tx, post_tx, delete_tx],
|
||||||
|
)
|
||||||
.mount("/static", FileServer::from("static"))
|
.mount("/static", FileServer::from("static"))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,8 @@
|
|||||||
<th>卖出价</th>
|
<th>卖出价</th>
|
||||||
<th>卖量</th>
|
<th>卖量</th>
|
||||||
<th><abbr title="卖出金额-佣金-各种手续费">实际卖出</abbr></th>
|
<th><abbr title="卖出金额-佣金-各种手续费">实际卖出</abbr></th>
|
||||||
<th>收益</th>
|
<th>收益: {{ gain|float|round(2) }} ({{ ((pct|float)*100)|round(2) }} %)</th>
|
||||||
<th>实际收益</th>
|
<th>实际收益: {{ net_gain|float|round(2) }} ({{ ((net_pct|float)*100)|round(2) }} %)</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
<th><label class="label">卖出日期</label></th>
|
<th><label class="label">卖出日期</label></th>
|
||||||
<th><label class="label">卖出价</label></th>
|
<th><label class="label">卖出价</label></th>
|
||||||
<th><label class="label">成交量</label></th>
|
<th><label class="label">成交量</label></th>
|
||||||
<th><label class="label">实际卖出入</label></th>
|
<th><label class="label">实际卖出</label></th>
|
||||||
<th style="min-width: 4rem; vertical-align: middle;">操作</th>
|
<th style="min-width: 4rem; vertical-align: middle;">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user