going somewhere.
This commit is contained in:
1 parent
494cdedfc6
commit
64e9be8b5a
12 files changed
+200
-73
No files matched your search
@@ -56,7 +56,7 @@ CREATE TABLE IF NOT EXISTS cashed_cheques
|
|||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
drawer_user_id INTEGER NOT NULL,
|
drawer_user_id INTEGER NOT NULL,
|
||||||
drawer_account_id INTEGER NOT NULL,
|
drawer_account_id INTEGER,
|
||||||
cheque_number INTEGER NOT NULL,
|
cheque_number INTEGER NOT NULL,
|
||||||
amount INTEGER NOT NULL,
|
amount INTEGER NOT NULL,
|
||||||
underlying_security INTEGER NOT NULL,
|
underlying_security INTEGER NOT NULL,
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ erDiagram
|
|||||||
users ||--o{ securities : owner
|
users ||--o{ securities : owner
|
||||||
security_types ||--o{ securities: s
|
security_types ||--o{ securities: s
|
||||||
securities ||--o{ accounts: ps
|
securities ||--o{ accounts: ps
|
||||||
|
securities ||--o{ security_permissions: i
|
||||||
|
users ||--o{ security_permissions: i
|
||||||
users ||--o{ cheques: ps
|
users ||--o{ cheques: ps
|
||||||
securities ||--o{ cheques: ps
|
securities ||--o{ cheques: ps
|
||||||
```
|
```
|
||||||
+43
-10
@@ -1,16 +1,23 @@
|
|||||||
use rusqlite::{Connection, params, Result, Row, Transaction};
|
use rusqlite::{Connection, OptionalExtension, params, Result, Row, Transaction};
|
||||||
|
use sha2::digest::typenum::op;
|
||||||
|
use crate::db::_external_id::{Id, AccountId, UserId};
|
||||||
|
|
||||||
use crate::db::_securities::Security;
|
use crate::db::_securities::Security;
|
||||||
use crate::db::_users::User;
|
use crate::db::_users::User;
|
||||||
|
use crate::db::external_id::SecurityId;
|
||||||
|
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
pub id: i64,
|
id: u32,
|
||||||
pub amount: i64,
|
amount: u32,
|
||||||
pub timestamp: i64,
|
timestamp: i32,
|
||||||
pub security: Security,
|
security: Security,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Account {
|
impl Account {
|
||||||
|
pub fn id(&self) -> AccountId {
|
||||||
|
AccountId::from_id(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_row(row: &Row) -> Result<Account> {
|
pub fn from_row(row: &Row) -> Result<Account> {
|
||||||
return Ok(Account {
|
return Ok(Account {
|
||||||
id: row.get("acc_id")?,
|
id: row.get("acc_id")?,
|
||||||
@@ -55,13 +62,21 @@ pub fn get_account_by_id(tx: &Transaction, id: u32) -> Result<Account> {
|
|||||||
return tx.query_row(q, params![id], Account::from_row);
|
return tx.query_row(q, params![id], Account::from_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_account(tx: &Transaction, sec: &Security, user: &User) -> Result<Account> {
|
pub fn create_account(tx: &Transaction, sec: &SecurityId, user: &UserId) -> Result<Account> {
|
||||||
let q = "INSERT INTO accounts(security, owner, amount, timestamp)
|
let q = "INSERT INTO accounts(security, owner, amount, timestamp)
|
||||||
VALUES (?1, ?2, 0, CURRENT_TIMESTAMP)";
|
VALUES (?1, ?2, 0, CURRENT_TIMESTAMP)";
|
||||||
tx.execute(q, (sec.id, user.id))?;
|
tx.execute(q, (sec, user))?;
|
||||||
return get_account_by_id(tx, tx.last_insert_rowid() as u32);
|
return get_account_by_id(tx, tx.last_insert_rowid() as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_owner(tx: &Transaction, account_id: &AccountId, user_id: &UserId) -> Result<bool> {
|
||||||
|
let q = "SELECT id FROM accounts WHERE id = ?1 AND owner = ?2";
|
||||||
|
let params = params![account_id, user_id];
|
||||||
|
let ite = |row: &Row| row.get::<usize, u32>(0);
|
||||||
|
let option = tx.query_row(q, params, ite).optional()?;
|
||||||
|
Ok(option.is_some())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
@@ -84,13 +99,14 @@ mod test {
|
|||||||
return (conn, users, securities);
|
return (conn, users, securities);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_all_accounts(tx: &Transaction, users: &Vec<User>, secs: &Vec<Security>) -> Result<()> {
|
fn create_all_accounts(tx: &Transaction, users: &Vec<User>, secs: &Vec<Security>) -> Result<Vec<Account>> {
|
||||||
|
let mut ret: Vec<Account> = Vec::new();
|
||||||
for sec in secs.iter() {
|
for sec in secs.iter() {
|
||||||
for user in users.iter() {
|
for user in users.iter() {
|
||||||
create_account(&tx, sec, user)?;
|
ret.push(create_account(&tx, &sec.id(), &user.id())?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -114,5 +130,22 @@ mod test {
|
|||||||
assert_eq!(0, accs.len());
|
assert_eq!(0, accs.len());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_owner() -> Result<()> {
|
||||||
|
let (mut conn, users, secs) = setup();
|
||||||
|
let tx = &conn.transaction()?;
|
||||||
|
let accounts = create_all_accounts(tx, &users, &secs)?;
|
||||||
|
let user_1 = users.get(0).unwrap();
|
||||||
|
let user_2 = users.get(1).unwrap();
|
||||||
|
let sec = secs.get(0).unwrap();
|
||||||
|
|
||||||
|
let acc = create_account(&tx, &sec.id(), &user_1.id())?;
|
||||||
|
|
||||||
|
assert!(is_owner(tx, &acc.id(), &user_1.id())?);
|
||||||
|
assert!(!is_owner(tx, &acc.id(), &user_2.id())?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+10
-13
@@ -1,36 +1,33 @@
|
|||||||
use rusqlite::{Result, Transaction};
|
use rusqlite::{Result, Transaction};
|
||||||
|
|
||||||
use crate::db::_external_id::{AccountId, SecurityId};
|
use crate::db::_external_id::{AccountId, SecurityId, UserId};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SecurityEmitterCheque {
|
pub struct SecurityEmitterCheque {
|
||||||
pub drawer_user_id: AccountId,
|
pub drawer_user_id: UserId,
|
||||||
pub security_id: SecurityId,
|
pub security_id: SecurityId,
|
||||||
pub payee_user_id: AccountId,
|
pub payee_user_id: UserId,
|
||||||
pub cheque_number: u32,
|
pub cheque_number: u32,
|
||||||
pub amount: u32,
|
pub amount: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecurityEmitterCheque {
|
impl SecurityEmitterCheque {
|
||||||
fn new_safe(tx: &Transaction,
|
pub fn new(drawer_user_id: UserId,
|
||||||
drawer_user_id: AccountId,
|
|
||||||
security_id: SecurityId,
|
security_id: SecurityId,
|
||||||
payee_user_id: AccountId,
|
payee_user_id: UserId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
id_generator: fn() -> u32) -> Result<SecurityEmitterCheque> {
|
cheque_number: u32) -> SecurityEmitterCheque {
|
||||||
Ok(SecurityEmitterCheque {
|
SecurityEmitterCheque {
|
||||||
drawer_user_id,
|
drawer_user_id,
|
||||||
security_id,
|
security_id,
|
||||||
payee_user_id,
|
payee_user_id,
|
||||||
cheque_number: id_generator(),
|
cheque_number,
|
||||||
amount,
|
amount,
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_valid(&self, tx: &Transaction) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,13 @@ macro_rules! impl_ids {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl rusqlite::ToSql for $T {
|
||||||
|
#[inline]
|
||||||
|
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
|
||||||
|
Ok(rusqlite::types::ToSqlOutput::from(self.0.get_id()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Id for $T {
|
impl Id for $T {
|
||||||
type Output = $T;
|
type Output = $T;
|
||||||
|
|
||||||
@@ -162,7 +169,7 @@ impl ExternalId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
|
|||||||
+17
-10
@@ -1,5 +1,6 @@
|
|||||||
use rusqlite::{Connection, OptionalExtension, params, Result, Row, ToSql, Transaction};
|
use rusqlite::{Connection, OptionalExtension, params, Result, Row, ToSql, Transaction};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use crate::db::_external_id::{Id, SecurityId, UserId};
|
||||||
use crate::db::users::User;
|
use crate::db::users::User;
|
||||||
|
|
||||||
|
|
||||||
@@ -33,11 +34,17 @@ impl ToSql for SecurityType {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, PartialEq)]
|
#[derive(Debug, Serialize, PartialEq)]
|
||||||
pub struct Security {
|
pub struct Security {
|
||||||
pub id: i32,
|
id: u32,
|
||||||
security_type: SecurityType,
|
security_type: SecurityType,
|
||||||
symbol: String,
|
symbol: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Security {
|
||||||
|
pub fn id(&self) -> SecurityId {
|
||||||
|
SecurityId::from_id(self.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Security {
|
impl Security {
|
||||||
pub fn from_row(row: &Row) -> Result<Security> {
|
pub fn from_row(row: &Row) -> Result<Security> {
|
||||||
Ok(Security {
|
Ok(Security {
|
||||||
@@ -56,7 +63,7 @@ pub struct NewSecurity {
|
|||||||
|
|
||||||
pub fn create_security(tx: &Transaction,
|
pub fn create_security(tx: &Transaction,
|
||||||
sec: &NewSecurity,
|
sec: &NewSecurity,
|
||||||
owner: &User) -> Result<Security> {
|
owner: &UserId) -> Result<Security> {
|
||||||
let q = "INSERT INTO
|
let q = "INSERT INTO
|
||||||
securities(name, symbol, security_type)
|
securities(name, symbol, security_type)
|
||||||
VALUES(?1, ?2, ?3)";
|
VALUES(?1, ?2, ?3)";
|
||||||
@@ -67,18 +74,18 @@ pub fn create_security(tx: &Transaction,
|
|||||||
let q = "INSERT INTO
|
let q = "INSERT INTO
|
||||||
security_permissions(security_id, user_id)
|
security_permissions(security_id, user_id)
|
||||||
VALUES(?1, ?2)";
|
VALUES(?1, ?2)";
|
||||||
tx.execute(q, params![new_sec_id, owner.id])?;
|
tx.execute(q, params![new_sec_id, owner])?;
|
||||||
|
|
||||||
return get_security_by_id(tx, new_sec_id);
|
return get_security_by_id(tx, new_sec_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_owner(tx: &Transaction, sec: &Security, user: &User) -> Result<bool> {
|
pub fn is_owner(tx: &Transaction, sec_id: &SecurityId, user_id: &UserId) -> Result<bool> {
|
||||||
let q = "SELECT id
|
let q = "SELECT id
|
||||||
FROM security_permissions
|
FROM security_permissions
|
||||||
WHERE security_id = ?1 AND user_id = ?2
|
WHERE security_id = ?1 AND user_id = ?2
|
||||||
LIMIT 1";
|
LIMIT 1";
|
||||||
let ret: Option<u32> = tx.query_row(q,
|
let ret: Option<u32> = tx.query_row(q,
|
||||||
params![sec.id, user.id],
|
params![sec_id, user_id],
|
||||||
|r| r.get(0)).optional()?;
|
|r| r.get(0)).optional()?;
|
||||||
return Ok(ret.is_some());
|
return Ok(ret.is_some());
|
||||||
}
|
}
|
||||||
@@ -145,10 +152,10 @@ mod test {
|
|||||||
let not_owner = users.get(1).unwrap();
|
let not_owner = users.get(1).unwrap();
|
||||||
let sec = mock_currency_1();
|
let sec = mock_currency_1();
|
||||||
let tx = conn.transaction()?;
|
let tx = conn.transaction()?;
|
||||||
let sec = create_security(&tx, &sec, &owner)?;
|
let sec = create_security(&tx, &sec, &owner.id())?;
|
||||||
|
|
||||||
assert!(is_owner(&tx, &sec, &owner)?);
|
assert!(is_owner(&tx, &sec.id(), &owner.id())?);
|
||||||
assert!(!is_owner(&tx, &sec, ¬_owner)?);
|
assert!(!is_owner(&tx, &sec.id(), ¬_owner.id())?);
|
||||||
|
|
||||||
tx.commit()
|
tx.commit()
|
||||||
}
|
}
|
||||||
@@ -161,7 +168,7 @@ mod test {
|
|||||||
|
|
||||||
let sec = mock_currency_1();
|
let sec = mock_currency_1();
|
||||||
let tx = conn.transaction()?;
|
let tx = conn.transaction()?;
|
||||||
create_security(&tx, &sec, &user)?;
|
create_security(&tx, &sec, &user.id())?;
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
let ret = list_securities(&conn)?;
|
let ret = list_securities(&conn)?;
|
||||||
let expt = Security {
|
let expt = Security {
|
||||||
@@ -180,7 +187,7 @@ mod test {
|
|||||||
symbol: "B".to_string(),
|
symbol: "B".to_string(),
|
||||||
};
|
};
|
||||||
let tx = conn.transaction()?;
|
let tx = conn.transaction()?;
|
||||||
create_security(&tx, &sec, &user)?;
|
create_security(&tx, &sec, &user.id())?;
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
let ret = list_securities(&conn)?;
|
let ret = list_securities(&conn)?;
|
||||||
assert_eq!(2, ret.len());
|
assert_eq!(2, ret.len());
|
||||||
|
|||||||
+10
-5
@@ -2,6 +2,7 @@ use rusqlite::{Result, Transaction};
|
|||||||
|
|
||||||
use crate::db::{_securities, _users};
|
use crate::db::{_securities, _users};
|
||||||
use crate::db::_accounts::create_account;
|
use crate::db::_accounts::create_account;
|
||||||
|
use crate::db::_external_id::UserId;
|
||||||
use crate::db::_securities::{NewSecurity, Security, SecurityType};
|
use crate::db::_securities::{NewSecurity, Security, SecurityType};
|
||||||
use crate::db::_users::User;
|
use crate::db::_users::User;
|
||||||
use crate::db::accounts::Account;
|
use crate::db::accounts::Account;
|
||||||
@@ -28,22 +29,26 @@ pub fn setup_users(tx: &Transaction) -> Result<Vec<User>> {
|
|||||||
return Ok(vec![user_1, user_2]);
|
return Ok(vec![user_1, user_2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_securities(tx: &Transaction, owner: &User) -> Result<Vec<Security>> {
|
pub fn setup_securities(tx: &Transaction, owner: &UserId) -> Result<Vec<Security>> {
|
||||||
let sec_1 = _securities::create_security(tx, &mock_currency_1(), owner)?;
|
let sec_1 = _securities::create_security(tx, &mock_currency_1(), owner)?;
|
||||||
let sec_2 = _securities::create_security(tx, &mock_currency_2(), owner)?;
|
let sec_2 = _securities::create_security(tx, &mock_currency_2(), owner)?;
|
||||||
return Ok(vec![sec_1, sec_2]);
|
return Ok(vec![sec_1, sec_2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_users_and_securities(tx: &Transaction) -> Result<(Vec<User>, Vec<Security>)>{
|
pub fn setup_users_and_securities(tx: &Transaction) -> Result<(Vec<User>, Vec<Security>)> {
|
||||||
let users = setup_users(tx)?;
|
let users = setup_users(tx)?;
|
||||||
let securities = setup_securities(tx, users.get(0).unwrap())?;
|
let securities = setup_securities(tx, &users.get(0).unwrap().id())?;
|
||||||
Ok((users, securities))
|
Ok((users, securities))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_basic_environment(tx: &Transaction) -> Result<(Vec<User>, Vec<Security>, Vec<Account>)> {
|
pub fn setup_basic_environment(tx: &Transaction) -> Result<(Vec<User>, Vec<Security>, Vec<Account>)> {
|
||||||
let (users, securities) = setup_users_and_securities(tx)?;
|
let (users, securities) = setup_users_and_securities(tx)?;
|
||||||
|
|
||||||
let acc1 = create_account(tx, securities.get(0).unwrap(), users.get(0).unwrap())?;
|
let acc1 = create_account(tx,
|
||||||
let acc2 = create_account(tx, securities.get(1).unwrap(), users.get(1).unwrap())?;
|
&securities.get(0).unwrap().id(),
|
||||||
|
&users.get(0).unwrap().id())?;
|
||||||
|
let acc2 = create_account(tx,
|
||||||
|
&securities.get(1).unwrap().id(),
|
||||||
|
&users.get(1).unwrap().id())?;
|
||||||
Ok((users, securities, vec![acc1, acc2]))
|
Ok((users, securities, vec![acc1, acc2]))
|
||||||
}
|
}
|
||||||
+12
-6
@@ -1,21 +1,28 @@
|
|||||||
use rusqlite::{Connection, params, Result, Transaction, Row};
|
use rusqlite::{Connection, params, Result, Transaction, Row};
|
||||||
use serde::{Serialize};
|
use serde::{Serialize};
|
||||||
|
use crate::db::_external_id::{UserId, Id};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: i32,
|
id: u32,
|
||||||
name: String,
|
name: String,
|
||||||
public_key: String,
|
public_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_user(tx: & Transaction, name: &String, public_key: &String) -> Result<User>{
|
impl User {
|
||||||
|
pub fn id(&self) -> UserId {
|
||||||
|
UserId::from_id(self.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_user(tx: &Transaction, name: &String, public_key: &String) -> Result<User> {
|
||||||
tx.execute("INSERT INTO users (name, public_key) VALUES (?1, ?2)",
|
tx.execute("INSERT INTO users (name, public_key) VALUES (?1, ?2)",
|
||||||
(name, public_key))?;
|
(name, public_key))?;
|
||||||
return get_user_by_id(tx, tx.last_insert_rowid() as i32);
|
return get_user_by_id(tx, tx.last_insert_rowid() as i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _user_from_row(row: &Row) -> Result<User>{
|
fn _user_from_row(row: &Row) -> Result<User> {
|
||||||
Ok(User {
|
Ok(User {
|
||||||
id: row.get(0)?,
|
id: row.get(0)?,
|
||||||
name: row.get(1)?,
|
name: row.get(1)?,
|
||||||
@@ -23,15 +30,14 @@ fn _user_from_row(row: &Row) -> Result<User>{
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_user_by_id(tx: & Transaction, id: i32) -> Result<User>{
|
fn get_user_by_id(tx: &Transaction, id: i32) -> Result<User> {
|
||||||
let q = "SELECT id, name, public_key
|
let q = "SELECT id, name, public_key
|
||||||
FROM users
|
FROM users
|
||||||
WHERE id = ?1";
|
WHERE id = ?1";
|
||||||
tx.query_row(q, params![id], _user_from_row)
|
tx.query_row(q, params![id], _user_from_row)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_users(conn: & Connection) -> Result<Vec<User>>{
|
pub fn list_users(conn: &Connection) -> Result<Vec<User>> {
|
||||||
|
|
||||||
let mut stmt = conn.prepare("SELECT id, name, public_key FROM users")?;
|
let mut stmt = conn.prepare("SELECT id, name, public_key FROM users")?;
|
||||||
let ret: Vec<User> = stmt.query_map([], _user_from_row)?
|
let ret: Vec<User> = stmt.query_map([], _user_from_row)?
|
||||||
.filter(Result::is_ok)
|
.filter(Result::is_ok)
|
||||||
|
|||||||
+5
-1
@@ -5,8 +5,12 @@ mod _cheques;
|
|||||||
mod _external_id;
|
mod _external_id;
|
||||||
mod _test_utils;
|
mod _test_utils;
|
||||||
|
|
||||||
|
pub mod external_id {
|
||||||
|
pub use super::_external_id::*;
|
||||||
|
}
|
||||||
|
|
||||||
pub mod securities {
|
pub mod securities {
|
||||||
pub use super::_securities::{get_security_by_id, list_securities, Security};
|
pub use super::_securities::{get_security_by_id, list_securities, Security, is_owner};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod users {
|
pub mod users {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use crate::db::users::{create_user, list_users};
|
|||||||
mod migration;
|
mod migration;
|
||||||
mod db;
|
mod db;
|
||||||
mod services;
|
mod services;
|
||||||
|
mod util;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct Output<T> {
|
struct Output<T> {
|
||||||
|
|||||||
@@ -1,37 +1,62 @@
|
|||||||
use rusqlite::{params, Result, Transaction};
|
use rusqlite::{params, Params, Transaction};
|
||||||
|
|
||||||
use crate::db::accounts::get_account_by_id;
|
use crate::db::accounts::{Account, get_account_by_id};
|
||||||
use crate::db::cheques::SecurityEmitterCheque;
|
use crate::db::cheques::SecurityEmitterCheque;
|
||||||
|
use crate::db::external_id::AccountId;
|
||||||
|
use crate::db::securities;
|
||||||
use crate::db::securities::get_security_by_id;
|
use crate::db::securities::get_security_by_id;
|
||||||
|
use crate::util::*;
|
||||||
|
|
||||||
fn _add_securities_to_account(tx: &Transaction,
|
fn _add_securities_to_account(tx: &TransactionHelper,
|
||||||
cheque: &SecurityEmitterCheque) -> Result<()> {
|
cheque: &SecurityEmitterCheque,
|
||||||
|
account_id: &AccountId) -> CustomResult<()> {
|
||||||
|
let q = "UPDATE accounts
|
||||||
|
SET amount = amount + ?1
|
||||||
|
WHERE id = ?2";
|
||||||
|
tx.execute(q, params![cheque.amount, account_id])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _add_cheque_to_cashed_cheques_log(tx: &TransactionHelper,
|
||||||
|
cheque: &SecurityEmitterCheque,
|
||||||
|
account_id: &AccountId) -> Result<(), Error> {
|
||||||
let q = "
|
let q = "
|
||||||
INSERT INTO accounts() VALUES()
|
INSERT INTO cashed_cheques(
|
||||||
|
drawer_user_id,
|
||||||
|
cheque_number,
|
||||||
|
amount,
|
||||||
|
underlying_security
|
||||||
|
)
|
||||||
|
VALUES(?1, ?2, ?3, ?4)
|
||||||
";
|
";
|
||||||
tx.execute(q, params![cheque.amount])?;
|
tx.execute(q, params![cheque.drawer_user_id,
|
||||||
|
cheque.cheque_number,
|
||||||
|
cheque.amount,
|
||||||
|
cheque.security_id])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cash_cheque(tx: &Transaction,
|
fn cash_cheque(tx: &Transaction,
|
||||||
cheque: &SecurityEmitterCheque) -> Result<()> {
|
cheque: &SecurityEmitterCheque,
|
||||||
cheque.assert_valid(tx)?;
|
account_id: &AccountId) -> CustomResult<()> {
|
||||||
// move money to new account
|
// move money to new account
|
||||||
_add_securities_to_account(tx, cheque)?;
|
if !securities::is_owner(tx, &cheque.security_id, &cheque.drawer_user_id).map_err(Error::SqlError)? {
|
||||||
// create transaction log
|
return Err(Error::InvalidDrawer(format!("The drawer is the security owner: {:?}", cheque)));
|
||||||
// create cheque log
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_account(tx: &Transaction, account_id: u32) -> Result<()> {
|
|
||||||
let account = get_account_by_id(tx, account_id)?;
|
let myx = TransactionHelper::new(tx);
|
||||||
return Ok(());
|
_add_securities_to_account(&myx, cheque, account_id)?;
|
||||||
|
_add_cheque_to_cashed_cheques_log(&myx, cheque, account_id)?;
|
||||||
|
// create transaction log
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use rusqlite::{Connection, params, Result};
|
use rusqlite::{Connection, params};
|
||||||
|
use crate::db::accounts::create_account;
|
||||||
|
|
||||||
use crate::db::test_utils;
|
use crate::db::test_utils;
|
||||||
use crate::migration;
|
use crate::migration;
|
||||||
@@ -44,19 +69,36 @@ mod test {
|
|||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basic() -> Result<()> {
|
fn test_basic() -> Result<(), Error> {
|
||||||
let mut conn = setup();
|
let mut conn = setup();
|
||||||
let tx = conn.transaction()?;
|
let tx = conn.transaction().unwrap();
|
||||||
let (users, securities, accounts) = test_utils::setup_basic_environment(&tx)?;
|
let (users, securities, accounts) = test_utils::setup_basic_environment(&tx).unwrap();
|
||||||
tx.commit()?;
|
let sec = securities.get(0).unwrap();
|
||||||
|
let drawer = users.get(0).unwrap();
|
||||||
|
let payee = users.get(1).unwrap();
|
||||||
|
let amount: u32 = 1000;
|
||||||
|
let payee_account = create_account(&tx, & sec.id(), & payee.id()).unwrap();
|
||||||
|
|
||||||
let tx = conn.transaction()?;
|
tx.commit().unwrap();
|
||||||
// let cheque = SecurityEmitterCheque{
|
let tx = conn.transaction().unwrap();
|
||||||
// drawer_user_id: users.get_unchecked(0).id
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
|
// let tx = conn.transaction()?;
|
||||||
|
// SecurityEmitterCheque::new(securities.get())
|
||||||
|
let cheque = SecurityEmitterCheque::new(
|
||||||
|
drawer.id(),
|
||||||
|
sec.id(),
|
||||||
|
payee.id(),
|
||||||
|
amount,
|
||||||
|
99,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
cash_cheque(&tx, &cheque, &payee_account.id())?;
|
||||||
|
|
||||||
|
tx.commit().unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
use rusqlite::{Params, Transaction};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
SqlError(rusqlite::Error),
|
||||||
|
InvalidDrawer(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CustomResult<T, E = Error> = Result<T, E>;
|
||||||
|
|
||||||
|
pub struct TransactionHelper<'a> {
|
||||||
|
tx: &'a Transaction<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionHelper<'_> {
|
||||||
|
pub fn new<'a>(tx: &'a Transaction) -> TransactionHelper<'a> {
|
||||||
|
TransactionHelper { tx }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute<P: Params>(&self, sql: &str, params: P) -> CustomResult<usize> {
|
||||||
|
self.tx.execute(sql, params).map_err(Error::SqlError)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user