modifier tr + ajout/modifier ac

This commit is contained in:
thatscringebro
2026-03-13 11:41:57 -04:00
parent 9bfc0c4304
commit 460f6e9c94
8 changed files with 213 additions and 15 deletions

View File

@@ -11,6 +11,7 @@ pub struct App {
pub new_account: Account, pub new_account: Account,
pub new_transaction: Transaction, pub new_transaction: Transaction,
pub selected_transaction_input: TransactionInput, pub selected_transaction_input: TransactionInput,
pub selected_account_input: AccountInput,
pub current_input: String, pub current_input: String,
pub connection: Connection, pub connection: Connection,
} }
@@ -35,6 +36,7 @@ impl App {
new_account: Account::new(0, String::new(), 0.0, AccountType::Cash), new_account: Account::new(0, String::new(), 0.0, AccountType::Cash),
new_transaction: Transaction::new_empty(), new_transaction: Transaction::new_empty(),
selected_transaction_input: TransactionInput::Type, selected_transaction_input: TransactionInput::Type,
selected_account_input: AccountInput::Asset,
current_input: String::new(), current_input: String::new(),
connection: con, connection: con,
}; };
@@ -66,15 +68,24 @@ impl App {
} }
pub fn save_new_account(&mut self) { pub fn save_new_account(&mut self) {
let new = self.new_account.get_id() == 0;
self.next_ac_input();
let ac = data_layer::upsert_account(&self.connection, self.new_account.clone()); let ac = data_layer::upsert_account(&self.connection, self.new_account.clone());
if new {
self.acc_list.add_account(ac); self.acc_list.add_account(ac);
}
self.new_account = Account::new(0, String::new(), 0.0, AccountType::Cash); self.new_account = Account::new(0, String::new(), 0.0, AccountType::Cash);
self.current_input = String::new();
self.selected_account_input = AccountInput::Asset;
} }
pub fn save_new_tr(&mut self) { pub fn save_new_tr(&mut self) {
let new = self.new_transaction.get_id() == 0;
self.next_tr_input(); self.next_tr_input();
let tr = data_layer::upsert_transaction(&self.connection, self.new_transaction.clone()); let tr = data_layer::upsert_transaction(&self.connection, self.new_transaction.clone());
if new {
self.trx_table.add_tr(tr); self.trx_table.add_tr(tr);
}
self.new_transaction = Transaction::new_empty(); self.new_transaction = Transaction::new_empty();
self.current_input = String::new(); self.current_input = String::new();
self.selected_transaction_input = TransactionInput::Type; self.selected_transaction_input = TransactionInput::Type;
@@ -119,7 +130,7 @@ impl App {
); );
} }
} }
self.set_current_input(); self.set_current_input_tr();
} }
pub fn previous_tr_input(&mut self) { pub fn previous_tr_input(&mut self) {
match self.selected_transaction_input { match self.selected_transaction_input {
@@ -160,9 +171,57 @@ impl App {
); );
} }
} }
self.set_current_input(); self.set_current_input_tr();
} }
fn set_current_input(&mut self) { pub fn next_ac_input(&mut self) {
match self.selected_account_input {
AccountInput::Asset => {
self.selected_account_input = AccountInput::Name;
self.new_account.set_asset_price(
self.current_input
.parse::<f64>()
.expect("Failed to parse data"),
);
}
AccountInput::Name => {
self.selected_account_input = AccountInput::Type;
self.new_account.set_name(self.current_input.clone());
}
AccountInput::Type => {
self.new_account.set_type(AccountType::from_usize(
self.current_input
.parse::<usize>()
.expect("Failed to parse data"),
));
}
}
self.set_current_input_ac();
}
pub fn previous_ac_input(&mut self) {
match self.selected_account_input {
AccountInput::Asset => {
self.new_account.set_asset_price(
self.current_input
.parse::<f64>()
.expect("Failed to parse data"),
);
}
AccountInput::Name => {
self.new_account.set_name(self.current_input.clone());
self.selected_account_input = AccountInput::Asset;
}
AccountInput::Type => {
self.new_account.set_type(AccountType::from_usize(
self.current_input
.parse::<usize>()
.expect("Failed to parse data"),
));
self.selected_account_input = AccountInput::Name;
}
}
self.set_current_input_ac();
}
pub fn set_current_input_tr(&mut self) {
match self.selected_transaction_input { match self.selected_transaction_input {
TransactionInput::Type => { TransactionInput::Type => {
self.current_input = self.new_transaction.get_type().get_id().to_string() self.current_input = self.new_transaction.get_type().get_id().to_string()
@@ -183,4 +242,18 @@ impl App {
} }
} }
} }
pub fn set_current_input_ac(&mut self) {
match self.selected_account_input {
AccountInput::Asset => {
self.current_input = self.new_account.get_asset_price().to_string();
}
AccountInput::Name => {
self.current_input = self.new_account.get_name();
}
AccountInput::Type => {
let ac_type = self.new_account.get_ac_type() as usize;
self.current_input = ac_type.to_string();
}
}
}
} }

View File

@@ -45,17 +45,20 @@ pub fn upsert_account(con: &Connection, ac: Account) -> Account {
let query; let query;
let ac_type = ac.get_ac_type() as i64; let ac_type = ac.get_ac_type() as i64;
if ac.get_id() == 0 { if ac.get_id() == 0 {
query = "INSERT INTO Accounts (name, type) VALUES (?, ?) RETURNING id;"; query = "INSERT INTO Accounts (name, type, asset_price) VALUES (:name, :type, :asset_price) RETURNING id;";
} else { } else {
query = "UPDATE Accounts SET name = ?, type = ? WHERE id = ? RETURNING id;"; query = "UPDATE Accounts SET name = :name, type = :type, asset_price = :asset_price WHERE id = :id RETURNING id;";
} }
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query).unwrap();
statement.bind((1, &ac.get_name() as &str)).unwrap(); statement.bind((":name", &ac.get_name() as &str)).unwrap();
statement.bind((2, ac_type)).unwrap(); statement.bind((":type", ac_type)).unwrap();
statement
.bind((":asset_price", ac.get_asset_price()))
.unwrap();
if ac.get_id() != 0 { if ac.get_id() != 0 {
statement.bind((3, ac.get_id())).unwrap(); statement.bind((":id", ac.get_id())).unwrap();
} }
let id; let id;
@@ -95,15 +98,25 @@ pub fn get_account(con: &Connection, id: i64) -> Account {
} }
pub fn get_account_total(id: i64, con: &Connection) -> f64 { pub fn get_account_total(id: i64, con: &Connection) -> f64 {
let mut query = "SELECT SUM(amount) as total FROM Transactions".to_owned(); let mut query = "
SELECT
SUM(
CASE
WHEN a.type = 2 THEN t.asset_amount * a.asset_price
ELSE t.amount
END
) AS total
FROM Transactions t
JOIN Accounts a ON t.account_id = a.id"
.to_owned();
if id != 0 { if id != 0 {
query.push_str(" WHERE account_id = ?") query.push_str(" WHERE account_id = :id")
} }
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query).unwrap();
if id != 0 { if id != 0 {
statement.bind((1, id)).unwrap(); statement.bind((":id", id)).unwrap();
} }
if let Ok(State::Row) = statement.next() { if let Ok(State::Row) = statement.next() {
@@ -200,7 +213,7 @@ pub fn upsert_transaction(con: &Connection, tr: Transaction) -> Transaction {
RETURNING id;"; RETURNING id;";
} else { } else {
query = "UPDATE Transactions query = "UPDATE Transactions
SET account_id = :ac_id, type_id = :type_id, amount = :amnt, date = :date, description = :desc, asset_amount = asset_amnt SET account_id = :ac_id, type_id = :type_id, amount = :amnt, date = :date, description = :desc, asset_amount = :asset_amnt
WHERE id = :id RETURNING id;"; WHERE id = :id RETURNING id;";
} }

View File

@@ -27,12 +27,21 @@ impl Account {
pub fn get_name(&self) -> String { pub fn get_name(&self) -> String {
return self.name.clone(); return self.name.clone();
} }
pub fn set_name(&mut self, name: String) {
self.name = name;
}
pub fn get_asset_price(&self) -> f64 { pub fn get_asset_price(&self) -> f64 {
return self.asset_price; return self.asset_price;
} }
pub fn set_asset_price(&mut self, asset_price: f64) {
self.asset_price = asset_price;
}
pub fn get_ac_type(&self) -> AccountType { pub fn get_ac_type(&self) -> AccountType {
return AccountType::try_from(self.ac_type as i64).unwrap(); return AccountType::try_from(self.ac_type as i64).unwrap();
} }
pub fn set_type(&mut self, ac_type: AccountType) {
self.ac_type = ac_type;
}
pub fn get_total(&self, con: &Connection) -> f64 { pub fn get_total(&self, con: &Connection) -> f64 {
return data_layer::get_account_total(self.id, con); return data_layer::get_account_total(self.id, con);
} }
@@ -43,6 +52,17 @@ pub enum AccountType {
Cash = 1, Cash = 1,
Assets, Assets,
} }
impl AccountType {
pub fn from_usize(v: usize) -> Self {
const CASH: usize = AccountType::Cash as usize;
const ASSET: usize = AccountType::Assets as usize;
match v {
CASH => return Self::Cash,
ASSET => return Self::Assets,
_ => return Self::Cash,
}
}
}
impl TryFrom<i64> for AccountType { impl TryFrom<i64> for AccountType {
type Error = (); type Error = ();

View File

@@ -32,6 +32,13 @@ impl AccountList {
} }
} }
pub fn selected_ac(&self) -> Account {
if let Some(i) = self.state.selected() {
return self.accounts.get(i).unwrap().clone();
}
return Account::new(0, String::new(), 0.0, AccountType::Cash);
}
pub fn add_account(&mut self, ac: Account) { pub fn add_account(&mut self, ac: Account) {
self.accounts.push(ac); self.accounts.push(ac);
} }

View File

@@ -30,6 +30,13 @@ impl TrxTable {
return self.trx.clone(); return self.trx.clone();
} }
pub fn selected_tr(&self) -> Transaction {
if let Some(i) = self.state.selected() {
return self.trx.get(i).unwrap().clone();
}
return Transaction::new_empty();
}
pub fn add_tr(&mut self, tr: Transaction) { pub fn add_tr(&mut self, tr: Transaction) {
self.trx.insert(0, tr); self.trx.insert(0, tr);
} }

View File

@@ -17,3 +17,10 @@ pub enum TransactionInput {
Description, Description,
Asset, Asset,
} }
#[derive(PartialEq)]
pub enum AccountInput {
Asset,
Name,
Type,
}

View File

@@ -101,9 +101,33 @@ where
app.new_transaction.set_date(Local::now()); app.new_transaction.set_date(Local::now());
} }
}, },
KeyCode::Char('m') => match app.current_widget {
CurrentWidget::AccountList => {
app.current_screen = CurrentScreen::NewAccount;
app.new_account = app.acc_list.selected_ac();
app.set_current_input_ac();
}
CurrentWidget::TrxList => {
app.current_screen = CurrentScreen::NewTransaction;
app.new_transaction = app.trx_table.selected_tr();
app.set_current_input_tr();
}
},
_ => {} _ => {}
}, },
CurrentScreen::NewAccount => match key.code { CurrentScreen::NewAccount => match key.code {
KeyCode::Down => {
app.next_ac_input();
}
KeyCode::Up => {
app.previous_ac_input();
}
KeyCode::Backspace => {
app.current_input.pop();
}
KeyCode::Char(value) => {
app.current_input.push(value);
}
KeyCode::Enter => { KeyCode::Enter => {
app.save_new_account(); app.save_new_account();
app.current_screen = CurrentScreen::Main; app.current_screen = CurrentScreen::Main;

View File

@@ -112,6 +112,35 @@ pub fn ui(frame: &mut Frame, app: &mut App) {
let area = centered_rect(50, 40, frame.area()); let area = centered_rect(50, 40, frame.area());
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(popup, area); frame.render_widget(popup, area);
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints([
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(3),
])
.split(area);
let ac_asset = Paragraph::new(ac_input_str(AccountInput::Asset, app))
.style(ac_input_style(AccountInput::Asset, app))
.block(
Block::default()
.borders(Borders::BOTTOM)
.title("Asset Price"),
);
frame.render_widget(ac_asset, chunks[0]);
let ac_name = Paragraph::new(ac_input_str(AccountInput::Name, app))
.style(ac_input_style(AccountInput::Name, app))
.block(Block::default().borders(Borders::BOTTOM).title("Name"));
frame.render_widget(ac_name, chunks[1]);
let ac_type = Paragraph::new(ac_input_str(AccountInput::Type, app))
.style(ac_input_style(AccountInput::Type, app))
.block(Block::default().borders(Borders::BOTTOM).title("Type"));
frame.render_widget(ac_type, chunks[2]);
} }
if let CurrentScreen::NewTransaction = app.current_screen { if let CurrentScreen::NewTransaction = app.current_screen {
@@ -233,7 +262,25 @@ fn tr_input_str(input: TransactionInput, app: &mut App) -> String {
TransactionInput::Asset => app.new_transaction.get_asset_amnt().to_string(), TransactionInput::Asset => app.new_transaction.get_asset_amnt().to_string(),
} }
} }
fn ac_input_style(input: AccountInput, app: &mut App) -> Style {
if app.selected_account_input == input {
return SELECTED_STYLE;
}
return Style::default();
}
fn ac_input_str(input: AccountInput, app: &mut App) -> String {
if app.selected_account_input == input {
return app.current_input.clone();
}
match input {
AccountInput::Name => app.new_account.get_name(),
AccountInput::Type => {
let ac_type = app.new_account.get_ac_type() as usize;
return ac_type.to_string();
}
AccountInput::Asset => app.new_account.get_asset_price().to_string(),
}
}
impl From<&Account> for ListItem<'_> { impl From<&Account> for ListItem<'_> {
fn from(value: &Account) -> Self { fn from(value: &Account) -> Self {
let line = Line::styled( let line = Line::styled(