Lines
100 %
Functions
75 %
Branches
use crate::error::{FinanceError, SplitError};
use sqlx::types::Uuid;
use sqlx::types::chrono::{DateTime, Utc};
use sqlx::{Connection, query_file};
use supp_macro::Builder;
#[derive(Debug, sqlx::FromRow, Builder)]
#[builder(error_kind = "FinanceError")]
pub struct Split {
pub id: Uuid,
pub tx_id: Uuid,
pub account_id: Uuid,
pub commodity_id: Uuid,
pub reconcile_state: Option<bool>,
pub reconcile_date: Option<DateTime<Utc>>,
pub value_num: i64,
pub value_denom: i64,
pub lot_id: Option<Uuid>,
}
impl Split {
pub async fn commit<E>(&self, conn: &mut E) -> Result<(), FinanceError>
where
E: Connection<Database = sqlx::Postgres>,
{
let mut tr = conn.begin().await?;
query_file!(
"sql/split_insert.sql",
&self.id,
&self.tx_id,
&self.account_id,
&self.commodity_id,
self.reconcile_state,
self.reconcile_date,
&self.value_num,
&self.value_denom,
self.lot_id,
)
.execute(&mut *tr)
.await?;
tr.commit().await?;
Ok(())
#[cfg(test)]
mod split_tests {
use super::*;
use crate::account::{Account, AccountBuilder};
use crate::commodity::{Commodity, CommodityBuilder};
use crate::transaction::{Transaction, TransactionBuilder};
#[cfg(feature = "testlog")]
use env_logger;
use log;
use sqlx::PgPool;
use sqlx::types::chrono::Local;
use tokio::sync::OnceCell;
/// Context for keeping environment intact
static CONTEXT: OnceCell<()> = OnceCell::const_new();
static COMMODITY: OnceCell<Commodity> = OnceCell::const_new();
static ACCOUNT: OnceCell<Account> = OnceCell::const_new();
static TRANSACTION: OnceCell<Transaction> = OnceCell::const_new();
async fn setup(pool: &PgPool) {
CONTEXT
.get_or_init(|| async {
let _ = env_logger::builder()
.is_test(true)
.filter_level(log::LevelFilter::Trace)
.try_init();
})
.await;
COMMODITY
CommodityBuilder::new()
.fraction(1000)
.id(Uuid::new_v4())
.build()
.unwrap()
let mut conn = pool.acquire().await.unwrap();
COMMODITY.get().unwrap().commit(&mut *conn).await.unwrap();
ACCOUNT
.get_or_init(|| async { AccountBuilder::new().id(Uuid::new_v4()).build().unwrap() })
ACCOUNT.get().unwrap().commit(&mut *conn).await.unwrap();
TRANSACTION
TransactionBuilder::new()
.post_date(Local::now().into())
.enter_date(Local::now().into())
sqlx::query_file!(
"sql/transaction_insert.sql",
&TRANSACTION.get().unwrap().id,
&TRANSACTION.get().unwrap().post_date,
&TRANSACTION.get().unwrap().enter_date
.execute(&mut *conn)
.await
.unwrap();
#[sqlx::test(migrations = "../migrations")]
async fn test_split_store(pool: PgPool) -> anyhow::Result<()> {
setup(&pool).await;
let split = Split {
id: Uuid::new_v4(),
tx_id: TRANSACTION.get().unwrap().id,
account_id: ACCOUNT.get().unwrap().id,
commodity_id: COMMODITY.get().unwrap().id,
value_num: 100,
value_denom: 1,
reconcile_state: None,
reconcile_date: None,
lot_id: None,
};
let mut conn = pool.acquire().await?;
sqlx::query!(
"SELECT post_date FROM transactions WHERE id = $1",
TRANSACTION.get().unwrap().id
.fetch_one(&mut *conn)
let mut conn = pool.begin().await?;
sqlx::query!("INSERT INTO splits (id, tx_id, account_id, commodity_id, value_num, value_denom) VALUES ($1, $2, $3, $4, $5, $6)",
&split.id,
&split.tx_id,
&split.account_id,
&split.commodity_id,
&split.value_num,
&split.value_denom)
?;
conn.commit().await?;
let result = sqlx::query!("SELECT id FROM splits WHERE value_num = 100")
assert_eq!(split.id, result.id);
let split2 = Split {
value_num: 200,
..split
split2.commit(&mut *conn).await?;
let result = sqlx::query!("SELECT id FROM splits WHERE value_num = 200")
assert_eq!(split2.id, result.id);
async fn test_split_builder(pool: PgPool) -> anyhow::Result<()> {
let split = Split::builder()
.tx_id(TRANSACTION.get().unwrap().id)
.account_id(ACCOUNT.get().unwrap().id)
.commodity_id(COMMODITY.get().unwrap().id)
.value_num(100)
.value_denom(1)
.build()?;
assert_eq!(split.value_num, 100);