Compare commits
3 Commits
v0.2.0
...
f7b645895d
| Author | SHA1 | Date | |
|---|---|---|---|
| f7b645895d | |||
| 14d0d82719 | |||
| c9e005475c |
+12
-3
@@ -55,6 +55,11 @@ enum Command {
|
|||||||
/// The number of days to look back for transactions
|
/// The number of days to look back for transactions
|
||||||
#[arg(short, long, default_value_t = 30)]
|
#[arg(short, long, default_value_t = 30)]
|
||||||
days: i64,
|
days: i64,
|
||||||
|
|
||||||
|
/// Filter transactions by a search term in the payee field. This will match any transaction
|
||||||
|
/// whose payee contains the search term, case-insensitive.
|
||||||
|
#[arg(short, long)]
|
||||||
|
filter: Option<String>,
|
||||||
},
|
},
|
||||||
/// Convert from a bank export to a CSV you can import manually to YNAB
|
/// Convert from a bank export to a CSV you can import manually to YNAB
|
||||||
Convert {
|
Convert {
|
||||||
@@ -141,6 +146,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
account,
|
account,
|
||||||
account_id,
|
account_id,
|
||||||
days,
|
days,
|
||||||
|
filter,
|
||||||
} => {
|
} => {
|
||||||
let client = Client::new(&token);
|
let client = Client::new(&token);
|
||||||
let plan = match (plan, plan_id) {
|
let plan = match (plan, plan_id) {
|
||||||
@@ -157,12 +163,15 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let transactions = match account {
|
let transactions = match account {
|
||||||
Some(account) => account.list_transactions(days).await?,
|
Some(account) => account.list_transactions(days, filter.as_deref()).await?,
|
||||||
None => plan.list_transactions(None, days).await?,
|
None => {
|
||||||
|
plan.list_transactions(None, days, filter.as_deref())
|
||||||
|
.await?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
println!("Transactions{} in the last 30 days:", accountstr);
|
println!("Transactions{} in the last 30 days:", accountstr);
|
||||||
for t in transactions {
|
for t in transactions {
|
||||||
println!("{},{},{}", t.date, t.payee, t.amount);
|
println!("{},{},{}", t.date, t.payee, t.format_amount());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+2
-4
@@ -18,9 +18,7 @@ fn parse_amount_str(amount_str: &str) -> Result<i64, Error> {
|
|||||||
let frac = format!("{:0<2}", frac);
|
let frac = format!("{:0<2}", frac);
|
||||||
let frac = frac.parse::<i64>()?;
|
let frac = frac.parse::<i64>()?;
|
||||||
|
|
||||||
let mut amount: i64 = 0;
|
let amount = whole * 100 * 10 + frac * 10;
|
||||||
amount += whole * 100 * 10;
|
|
||||||
amount += frac * 10;
|
|
||||||
Ok(if negative { -amount } else { amount })
|
Ok(if negative { -amount } else { amount })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +32,7 @@ impl Transaction {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_amount(&self) -> String {
|
pub fn format_amount(&self) -> String {
|
||||||
let whole = self.amount / 10 / 100;
|
let whole = self.amount / 10 / 100;
|
||||||
let frac = if self.amount < 0 { -1 } else { 1 } * (self.amount / 10) % 100;
|
let frac = if self.amount < 0 { -1 } else { 1 } * (self.amount / 10) % 100;
|
||||||
format!("{}.{:02}", whole, frac)
|
format!("{}.{:02}", whole, frac)
|
||||||
|
|||||||
+10
-2
@@ -130,6 +130,7 @@ impl Plan {
|
|||||||
&self,
|
&self,
|
||||||
account_id: Option<&str>,
|
account_id: Option<&str>,
|
||||||
days: i64,
|
days: i64,
|
||||||
|
filter: Option<&str>,
|
||||||
) -> Result<Vec<Transaction>, Error> {
|
) -> Result<Vec<Transaction>, Error> {
|
||||||
let since_date = chrono::Local::now().date_naive() - chrono::Duration::days(days);
|
let since_date = chrono::Local::now().date_naive() - chrono::Duration::days(days);
|
||||||
let since_date = since_date.format("%Y-%m-%d");
|
let since_date = since_date.format("%Y-%m-%d");
|
||||||
@@ -139,6 +140,7 @@ impl Plan {
|
|||||||
Ok(transform::from_ynab_transactions(
|
Ok(transform::from_ynab_transactions(
|
||||||
&res.data.transactions,
|
&res.data.transactions,
|
||||||
account_id,
|
account_id,
|
||||||
|
filter,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,8 +177,14 @@ impl Account {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_transactions(&self, days: i64) -> Result<Vec<Transaction>, Error> {
|
pub async fn list_transactions(
|
||||||
self.plan.list_transactions(Some(&self.id), days).await
|
&self,
|
||||||
|
days: i64,
|
||||||
|
filter: Option<&str>,
|
||||||
|
) -> Result<Vec<Transaction>, Error> {
|
||||||
|
self.plan
|
||||||
|
.list_transactions(Some(&self.id), days, filter)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn upload(&self, transactions: &[Transaction]) -> Result<(), Error> {
|
pub async fn upload(&self, transactions: &[Transaction]) -> Result<(), Error> {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ pub(crate) fn to_ynab_transactions(
|
|||||||
pub(crate) fn from_ynab_transactions(
|
pub(crate) fn from_ynab_transactions(
|
||||||
transactions: &[apitypes::Transaction],
|
transactions: &[apitypes::Transaction],
|
||||||
account_id: Option<&str>,
|
account_id: Option<&str>,
|
||||||
|
filter: Option<&str>,
|
||||||
) -> Vec<Transaction> {
|
) -> Vec<Transaction> {
|
||||||
transactions
|
transactions
|
||||||
.iter()
|
.iter()
|
||||||
@@ -38,6 +39,15 @@ pub(crate) fn from_ynab_transactions(
|
|||||||
Some(id) => t.account_id == id,
|
Some(id) => t.account_id == id,
|
||||||
None => true,
|
None => true,
|
||||||
})
|
})
|
||||||
|
.filter(|t| match filter {
|
||||||
|
Some(filter) => t
|
||||||
|
.payee_name
|
||||||
|
.to_owned()
|
||||||
|
.unwrap_or(String::new())
|
||||||
|
.to_lowercase()
|
||||||
|
.contains(filter.to_lowercase().as_str()),
|
||||||
|
None => true,
|
||||||
|
})
|
||||||
.map(|t| Transaction {
|
.map(|t| Transaction {
|
||||||
date: t.date.to_owned(),
|
date: t.date.to_owned(),
|
||||||
payee: match &t.payee_name {
|
payee: match &t.payee_name {
|
||||||
|
|||||||
Reference in New Issue
Block a user