Started to implement account history.
This commit is contained in:
1 parent
20b5f32c71
commit
f4593df95b
8 files changed
+154
-6
No files matched your search
@@ -0,0 +1,99 @@
|
||||
use rusqlite::{Connection, OptionalExtension, params};
|
||||
use crate::db::accounts;
|
||||
use crate::db::accounts::Account;
|
||||
use crate::db::external_id::AccountId;
|
||||
use crate::util::{CustomResult, Error};
|
||||
|
||||
pub struct AccountHistory {
|
||||
account: Account,
|
||||
timestamps: Vec<i64>,
|
||||
amounts: Vec<i64>,
|
||||
}
|
||||
|
||||
impl AccountHistory {
|
||||
pub fn account(&self) -> &Account {
|
||||
&self.account
|
||||
}
|
||||
pub fn timestamps(&self) -> &Vec<i64> {
|
||||
&self.timestamps
|
||||
}
|
||||
pub fn amount(&self) -> &Vec<i64> {
|
||||
&self.amounts
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_account_history(tx: &Connection, account_id: &AccountId) -> CustomResult<AccountHistory> {
|
||||
let q = "
|
||||
SELECT amount, CAST(timestamp AS INT) AS timestamp
|
||||
FROM accounts_history
|
||||
WHERE account_id = ?1
|
||||
ORDER BY account_version ASC
|
||||
";
|
||||
|
||||
let mut stmt = tx.prepare(q).map_err(Error::SqlError)?;
|
||||
let rows = stmt
|
||||
.query_map(params![account_id], |row| Ok((row.get("amount")?, row.get("timestamp")?)))
|
||||
.optional()
|
||||
.map_err(Error::SqlError)?;
|
||||
|
||||
let ret: Vec<(i64, i64)> = rows.map(|x|
|
||||
x.map(Result::unwrap)
|
||||
.collect()).unwrap_or(vec![]);
|
||||
|
||||
let account = accounts::get_account_by_id(tx, account_id)?;
|
||||
let mut amounts: Vec<i64> = ret.iter().map(|r| r.0).collect();
|
||||
let mut timestamps: Vec<i64> = ret.iter().map(|r| r.1).collect();
|
||||
amounts.push(account.amount());
|
||||
timestamps.push(account.timestamp());
|
||||
|
||||
Ok(AccountHistory{account, timestamps, amounts })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_account_history {
|
||||
use crate::db::test_utils::setup_basic_environment;
|
||||
use crate::migration;
|
||||
use crate::util::CustomResult;
|
||||
use super::*;
|
||||
|
||||
fn setup() -> CustomResult<(Connection, Account)> {
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
|
||||
migration::apply_migrations(&mut conn).unwrap();
|
||||
let ret = {
|
||||
let mut tx = conn.transaction().unwrap();
|
||||
let (_, _, accounts) = setup_basic_environment(&mut tx)?;
|
||||
tx.commit().unwrap();
|
||||
accounts.get(0).unwrap().clone()
|
||||
};
|
||||
Ok((conn, ret))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_account_history_trivial() -> CustomResult<()>{
|
||||
let (mut conn, acc) = setup()?;
|
||||
let acc_hist = get_account_history(&conn, &acc.id())?;
|
||||
assert_eq!(acc_hist.amounts, vec![0, 0]);
|
||||
assert_eq!(acc_hist.timestamps.len(), 2);
|
||||
assert_eq!(acc_hist.account.label(), "acc1");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_account_history_with_transactions() -> CustomResult<()>{
|
||||
let (mut conn, acc) = setup()?;
|
||||
{
|
||||
let tx = conn.transaction().unwrap();
|
||||
accounts::add_to_account(&tx, &acc.id(), 1)?;
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
|
||||
let tx = conn.transaction().unwrap();
|
||||
let acc_hist = get_account_history(&tx, &acc.id())?;
|
||||
assert_eq!(acc_hist.amounts, vec![0, 1, 1]);
|
||||
assert_eq!(acc_hist.timestamps.len(), 3);
|
||||
assert_eq!(acc_hist.account.label(), "acc1");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,15 @@ impl ToString for AccountType{
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for AccountType {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
AccountType::Checking => AccountType::Checking,
|
||||
AccountType::SecurityEmitter => AccountType::SecurityEmitter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Account {
|
||||
id: i64,
|
||||
amount: i64,
|
||||
@@ -97,6 +106,19 @@ impl Account {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Account{
|
||||
fn clone(&self) -> Self {
|
||||
Account{
|
||||
id: self.id,
|
||||
amount: self.amount,
|
||||
timestamp: self.timestamp,
|
||||
security: self.security.clone(),
|
||||
account_type: self.account_type.clone(),
|
||||
label: self.label.clone(),
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct AccountSnapshot {
|
||||
amount: i64,
|
||||
|
||||
@@ -233,7 +233,7 @@ pub mod test_external_ids {
|
||||
#[test]
|
||||
fn test_micro_id() {
|
||||
let sid = SecurityId::from_id(33);
|
||||
assert_eq!(sid.micro_id(), "s-ea676268")
|
||||
assert_eq!(sid.micro_id(), "s-ea6762")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -87,6 +87,17 @@ impl Security {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Security {
|
||||
fn clone(&self) -> Self {
|
||||
Security{
|
||||
id: self.id,
|
||||
security_type: self.security_type.clone(),
|
||||
symbol: self.symbol.clone(),
|
||||
name: self.name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NewSecurity {
|
||||
pub security_type: SecurityType,
|
||||
pub symbol: String,
|
||||
|
||||
@@ -6,6 +6,7 @@ mod _ledger_internal;
|
||||
mod _securities;
|
||||
mod _test_utils;
|
||||
mod _users;
|
||||
mod _account_history;
|
||||
|
||||
pub mod ledger_internal {
|
||||
pub use super::_ledger_internal::*;
|
||||
@@ -33,6 +34,10 @@ pub mod accounts {
|
||||
};
|
||||
}
|
||||
|
||||
pub mod account_history{
|
||||
pub use super::_account_history::get_account_history;
|
||||
}
|
||||
|
||||
pub mod cheques {
|
||||
pub use super::_cheques::{Cheque, Drawer, Payee, WireTransferRequest};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::db::external_id::{AccountId, Id, SecurityId, UserId};
|
||||
use crate::util::{CustomResult, Error};
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use crate::db::accounts;
|
||||
use crate::db::{account_history, accounts};
|
||||
use crate::db::securities::Security;
|
||||
use crate::services::_securities::SecurityDTO;
|
||||
|
||||
@@ -22,6 +22,11 @@ pub struct AccountDTO {
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct AccountHistoryDTO {
|
||||
|
||||
}
|
||||
|
||||
impl AccountDTO {
|
||||
fn from_entity(account: & Account) -> AccountDTO {
|
||||
AccountDTO{
|
||||
@@ -66,4 +71,10 @@ impl AccountService<'_> {
|
||||
let ret = &accounts::get_account_by_id(self.conn, account_id)?;
|
||||
Ok(AccountDTO::from_entity(ret))
|
||||
}
|
||||
|
||||
pub fn get_account_history_by_id(&self, account_id: &AccountId, user_id: &UserId) -> CustomResult<()> {
|
||||
let ret = account_history::get_account_history(self.conn, account_id)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -70,8 +70,8 @@ fn create_views_scope() -> Scope {
|
||||
.service(index_views::login)
|
||||
.service(index_views::logout)
|
||||
.service(views::actions)
|
||||
.service(views::accounts)
|
||||
.service(views::account)
|
||||
.service(views::get_accounts)
|
||||
.service(views::get_account)
|
||||
.service(views::securities)
|
||||
.service(views::security)
|
||||
.service(views::users)
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ pub async fn actions(tmpl_env: MiniJinjaRenderer) -> actix_web::Result<impl Resp
|
||||
}
|
||||
|
||||
#[actix_web::get("/accounts")]
|
||||
pub async fn accounts(tmpl_env: MiniJinjaRenderer,
|
||||
pub async fn get_accounts(tmpl_env: MiniJinjaRenderer,
|
||||
req: HttpRequest,
|
||||
service_factory: web::Data<ServicesFactory>) -> actix_web::Result<impl Responder> {
|
||||
let conn = service_factory.get_connection()?;
|
||||
@@ -34,7 +34,7 @@ pub async fn accounts(tmpl_env: MiniJinjaRenderer,
|
||||
}
|
||||
|
||||
#[actix_web::get("/accounts/{account_id}")]
|
||||
pub async fn account(tmpl_env: MiniJinjaRenderer,
|
||||
pub async fn get_account(tmpl_env: MiniJinjaRenderer,
|
||||
req: HttpRequest,
|
||||
service_factory: web::Data<ServicesFactory>) -> actix_web::Result<impl Responder> {
|
||||
let path_event_type = req.match_info().get("account_id").unwrap();
|
||||
|
||||
Reference in new issue
Block a user