diff --git a/README.md b/README.md
index 8fc7ae1..842f2a5 100644
--- a/README.md
+++ b/README.md
@@ -39,13 +39,16 @@ nix develop
#### Architecture
-* [x] put all the ressources (migrations, templates and statics) into a ressources directory
+* [x] put all the resources (migrations, templates and statics) into a resources directory
#### Backend
* [ ] Parametrize `CURRENT_TIMESTAMP`
* [ ] Make certain the user that creates the event is allowed to do it.
-* [ ] sd
+* [ ] Add interest rates to currency accounts
+* [ ] Add market cap table to be able to track market cap by time
+* [ ] Add a minimum amount of security to exchange
+
#### Front end
diff --git a/resources/templates/account.html b/resources/templates/account.html
index 4a785b3..abe7213 100644
--- a/resources/templates/account.html
+++ b/resources/templates/account.html
@@ -8,8 +8,8 @@
Summary
- Micro ID {{ account.id | micro_id }}
- Account ID {{ account.id }}
+ Micro ID {{ account.id | micro_id }}
+ Account ID {{ account.id }}
Label {{ account.label }}
Amount {{ account.amount | thousand_separator }}
Account Type {{ account.account_type | replace("_", " ") | lower}}
diff --git a/resources/templates/accounts.html b/resources/templates/accounts.html
index 0d27a1e..fbc1247 100644
--- a/resources/templates/accounts.html
+++ b/resources/templates/accounts.html
@@ -21,7 +21,7 @@
{% for account in accounts %}
- {{ account.id | micro_id}}
+ {{ account.id | micro_id}}
{{ account.label }}
{{ account.amount | thousand_separator }}
{{ account.account_type | replace("_", " ") | lower}}
diff --git a/resources/templates/securities.html b/resources/templates/securities.html
index 51f9a34..3f520f3 100644
--- a/resources/templates/securities.html
+++ b/resources/templates/securities.html
@@ -20,10 +20,10 @@
{% for sec in securities %}
- {{ sec.id | micro_id }}
+ {{ sec.id | micro_id }}
{{ sec.name }}
- {{ sec.symbol }}
- {{ sec.security_type }}
+ {{ sec.symbol | lower }}
+ {{ sec.security_type }}
{% endfor %}
diff --git a/resources/templates/security.html b/resources/templates/security.html
index a9eb4d9..a2a48a3 100644
--- a/resources/templates/security.html
+++ b/resources/templates/security.html
@@ -4,7 +4,18 @@
{% block body %}
-Security!
+
+ Summary
+ Micro ID {{ security.id | micro_id }}
+ Account ID {{ security.id }}
+ Name {{ security.name }}
+ Symbol {{ security.symbol | lower }}
+ Security Type {{ security.security_type }}
+ Number of Checking Accounts {{ account_numbers.checking }}
+ Number of Security Emitter Accounts {{ account_numbers.security_emitter }}
+ Market Cap {{ market_cap | thousand_separator}}
+
+
{% endblock %}
\ No newline at end of file
diff --git a/src/db/_accounts.rs b/src/db/_accounts.rs
index 8aed5d6..83481b4 100644
--- a/src/db/_accounts.rs
+++ b/src/db/_accounts.rs
@@ -397,6 +397,31 @@ pub fn add_to_account(
return ret;
}
+pub fn get_number_of_accounts(tx: &Connection,
+ sec: &SecurityId,
+ account_type: AccountType) -> CustomResult{
+ let q = "
+ SELECT COUNT(*) AS count
+ FROM accounts
+ WHERE security = ?
+ AND account_type = ?
+ ";
+ let params = params![sec, account_type.to_string()];
+ tx.query_row(q, params, |row| row.get("count")).map_err(Error::SqlError)
+}
+
+pub fn get_security_market_cap(tx: &Connection,
+ sec: &SecurityId) -> CustomResult {
+ let q = "
+ SELECT -1 * SUM(amount) AS count
+ FROM accounts
+ WHERE security = ?
+ AND account_type = ?
+ ";
+ let params = params![sec, AccountType::SecurityEmitter.to_string()];
+ tx.query_row(q, params, |row| row.get("count")).map_err(Error::SqlError)
+}
+
#[cfg(test)]
mod test_accounts {
use rusqlite::{Connection, Transaction};
diff --git a/src/db/_securities.rs b/src/db/_securities.rs
index 6ad6aa1..ec5cc3a 100644
--- a/src/db/_securities.rs
+++ b/src/db/_securities.rs
@@ -1,4 +1,4 @@
-use rusqlite::{params, Connection, Result, Row, ToSql, Transaction};
+use rusqlite::{params, Connection, Result, Row, ToSql, Transaction, OptionalExtension};
use serde::Serialize;
use crate::db::_external_id::{Id, SecurityId};
@@ -104,18 +104,19 @@ pub struct NewSecurity {
pub name: String,
}
-pub fn create_security(tx: &Transaction, sec: &NewSecurity) -> CustomResult {
+pub fn create_security(tx: &Connection, sec: &NewSecurity) -> CustomResult {
let q = "INSERT INTO
securities(name, symbol, security_type)
VALUES(?1, ?2, ?3)";
let params = (&sec.name, &sec.symbol, &sec.security_type);
tx.execute(q, params).map_err(Error::SqlError)?;
- let new_sec_id = tx.last_insert_rowid() as i32;
+ let new_sec_id:i64 = tx.last_insert_rowid();
- return get_security_by_id(tx, new_sec_id);
+ let ret = get_security_by_id(tx, &SecurityId::from_id(new_sec_id))?;
+ Ok(ret.unwrap())
}
-pub fn get_security_by_id(tx: &Transaction, id: i32) -> CustomResult {
+pub fn get_security_by_id(tx: &Connection, id: & SecurityId) -> CustomResult> {
let q = "SELECT id AS sec_id,
security_type AS sec_security_type,
symbol AS sec_symbol,
@@ -123,7 +124,9 @@ pub fn get_security_by_id(tx: &Transaction, id: i32) -> CustomResult {
FROM securities
WHERE id = ?1";
tx.query_row(q, params![id], Security::from_row)
+ .optional()
.map_err(Error::SqlError)
+
}
pub fn list_securities(conn: &Connection) -> CustomResult> {
diff --git a/src/db/mod.rs b/src/db/mod.rs
index 799f65b..dd02678 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -30,7 +30,8 @@ pub mod accounts {
pub use super::_accounts::{
add_to_account, create_checking_account, create_security_emitter_account,
get_account_by_id, is_owner, list_accounts, Account, AccountChangeReceipt,
- list_user_accounts, AccountType, get_checking_accounts_for_sec_id
+ list_user_accounts, AccountType, get_checking_accounts_for_sec_id, get_number_of_accounts,
+ get_security_market_cap
};
}
diff --git a/src/services/_accounts.rs b/src/services/_accounts.rs
index 66312bf..4ca9070 100644
--- a/src/services/_accounts.rs
+++ b/src/services/_accounts.rs
@@ -11,6 +11,11 @@ use crate::services::_securities::SecurityDTO;
pub struct AccountService<'a> {
conn: &'a Connection,
}
+#[derive(Deserialize, Serialize, Debug)]
+pub struct AccountNumbers {
+ checking: i64,
+ security_emitter: i64,
+}
#[derive(Deserialize, Serialize, Debug)]
pub struct AccountDTO {
@@ -42,8 +47,6 @@ impl AccountDTO {
}
-
-
impl AccountService<'_> {
pub fn new(conn: & Connection) -> AccountService{
@@ -80,7 +83,12 @@ impl AccountService<'_> {
amounts: ac.amount().clone(),
timestamps: ac.timestamps().clone(),
};
-
Ok(ret)
}
+
+ pub fn get_number_of_accounts(&self, sec_id: &SecurityId) -> CustomResult{
+ let checking = accounts::get_number_of_accounts(self.conn, sec_id, AccountType::Checking)?;
+ let security_emitter = accounts::get_number_of_accounts(self.conn, sec_id, AccountType::SecurityEmitter)?;
+ Ok(AccountNumbers{checking, security_emitter})
+ }
}
\ No newline at end of file
diff --git a/src/services/_securities.rs b/src/services/_securities.rs
index 849fc10..4c8f0fe 100644
--- a/src/services/_securities.rs
+++ b/src/services/_securities.rs
@@ -1,7 +1,9 @@
use crate::db::external_id::SecurityId;
-use crate::db::securities::{list_securities, Security};
+use crate::db::securities::{Security};
use rusqlite::Connection;
use serde::{Deserialize, Serialize};
+use crate::db::{accounts, securities};
+use crate::util::CustomResult;
#[derive(Serialize, Deserialize, Debug)]
pub struct SecurityDTO {
@@ -31,11 +33,20 @@ impl SecurityService<'_> {
SecurityService { conn }
}
- pub fn list_securities(self) -> Vec {
- list_securities(self.conn)
+ pub fn list_securities(&self) -> Vec {
+ securities::list_securities(self.conn)
.unwrap()
.iter()
.map(SecurityDTO::from_entity)
.collect()
}
+
+ pub fn get_security_by_id(&self, sec_id: &SecurityId) -> CustomResult> {
+ let ret = securities::get_security_by_id(self.conn, sec_id)?;
+ Ok(ret.map(|sec|SecurityDTO::from_entity(&sec)))
+ }
+
+ pub fn get_security_market_cap(&self, sec_id: &SecurityId)-> CustomResult {
+ accounts::get_security_market_cap(self.conn, sec_id)
+ }
}
diff --git a/src/webui/services.rs b/src/webui/services.rs
index 8cef64b..a9e53e6 100644
--- a/src/webui/services.rs
+++ b/src/webui/services.rs
@@ -65,6 +65,5 @@ impl ServicesFactory {
conn: &'a PooledConnection,
)-> Result {
Ok(AccountService::new(conn))
-
}
}
diff --git a/src/webui/views.rs b/src/webui/views.rs
index 0404412..45c5f18 100644
--- a/src/webui/views.rs
+++ b/src/webui/views.rs
@@ -65,10 +65,27 @@ pub async fn securities(tmpl_env: MiniJinjaRenderer,
}
#[actix_web::get("/securities/{sec_id}")]
-pub async fn security(tmpl_env: MiniJinjaRenderer,
- service_factory: web::Data) -> actix_web::Result {
+pub async fn security(req: HttpRequest,
+ tmpl_env: MiniJinjaRenderer,
+ service_factory: web::Data) -> actix_web::Result {
+ let conn = service_factory.get_connection()?;
+ let security_service = service_factory.get_security_service(& conn)?;
+ let account_service = service_factory.get_account_service(& conn)?;
+ let sec_id = &req.match_info()
+ .get("sec_id")
+ .and_then(|param|SecurityId::from_string(¶m.to_string()))
+ .ok_or(error::ErrorNotFound("Invalid sec-id parameters"))?;
- let ctx = context!{};
+
+ let sec = security_service.get_security_by_id(sec_id).map_err(error::ErrorInternalServerError)?;
+ if sec.is_none() {
+ return Err(error::ErrorNotFound(format!("No security with id {:}", sec_id)))
+ }
+ let ctx = context!{
+ security => sec.unwrap(),
+ account_numbers => account_service.get_number_of_accounts(sec_id).map_err(error::ErrorInternalServerError)?,
+ market_cap => security_service.get_security_market_cap(sec_id).map_err(error::ErrorInternalServerError)?,
+ };
tmpl_env.render("security.html", ctx)
}