use serde::{Deserialize, Serialize};
use crate::{AccountId, BankAccountNumber, CategoryId, ConnectionId, MerchantId, TransactionId};
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct Transaction {
#[serde(rename = "_id")]
pub id: TransactionId,
#[serde(rename = "_account")]
pub account: AccountId,
#[serde(rename = "_connection")]
pub connection: ConnectionId,
pub created_at: chrono::DateTime<chrono::Utc>,
pub date: chrono::DateTime<chrono::Utc>,
pub description: String,
#[serde(with = "rust_decimal::serde::arbitrary_precision")]
pub amount: rust_decimal::Decimal,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "rust_decimal::serde::arbitrary_precision_option"
)]
pub balance: Option<rust_decimal::Decimal>,
#[serde(rename = "type")]
pub kind: TransactionKind,
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
pub enriched_data: Option<EnrichedTransactionData>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub enum TransactionKind {
#[serde(rename = "CREDIT")]
Credit,
#[serde(rename = "DEBIT")]
Debit,
#[serde(rename = "PAYMENT")]
Payment,
#[serde(rename = "TRANSFER")]
Transfer,
#[serde(rename = "STANDING ORDER")]
StandingOrder,
#[serde(rename = "EFTPOS")]
Eftpos,
#[serde(rename = "INTEREST")]
Interest,
#[serde(rename = "FEE")]
Fee,
#[serde(rename = "TAX")]
Tax,
#[serde(rename = "CREDIT CARD")]
CreditCard,
#[serde(rename = "DIRECT DEBIT")]
DirectDebit,
#[serde(rename = "DIRECT CREDIT")]
DirectCredit,
#[serde(rename = "ATM")]
Atm,
#[serde(rename = "LOAN")]
Loan,
}
impl TransactionKind {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Credit => "CREDIT",
Self::Debit => "DEBIT",
Self::Payment => "PAYMENT",
Self::Transfer => "TRANSFER",
Self::StandingOrder => "STANDING ORDER",
Self::Eftpos => "EFTPOS",
Self::Interest => "INTEREST",
Self::Fee => "FEE",
Self::Tax => "TAX",
Self::CreditCard => "CREDIT CARD",
Self::DirectDebit => "DIRECT DEBIT",
Self::DirectCredit => "DIRECT CREDIT",
Self::Atm => "ATM",
Self::Loan => "LOAN",
}
}
pub const fn as_bytes(&self) -> &'static [u8] {
self.as_str().as_bytes()
}
}
impl std::str::FromStr for TransactionKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"CREDIT" => Ok(Self::Credit),
"DEBIT" => Ok(Self::Debit),
"PAYMENT" => Ok(Self::Payment),
"TRANSFER" => Ok(Self::Transfer),
"STANDING ORDER" => Ok(Self::StandingOrder),
"EFTPOS" => Ok(Self::Eftpos),
"INTEREST" => Ok(Self::Interest),
"FEE" => Ok(Self::Fee),
"TAX" => Ok(Self::Tax),
"CREDIT CARD" => Ok(Self::CreditCard),
"DIRECT DEBIT" => Ok(Self::DirectDebit),
"DIRECT CREDIT" => Ok(Self::DirectCredit),
"ATM" => Ok(Self::Atm),
"LOAN" => Ok(Self::Loan),
_ => Err(()),
}
}
}
impl std::convert::TryFrom<String> for TransactionKind {
type Error = ();
fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}
impl std::convert::TryFrom<&str> for TransactionKind {
type Error = ();
fn try_from(value: &str) -> Result<Self, Self::Error> {
value.parse()
}
}
impl std::fmt::Display for TransactionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct EnrichedTransactionData {
pub category: TransactionCategory,
pub merchant: TransactionMerchant,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct TransactionCategory {
#[serde(rename = "_id")]
pub id: CategoryId,
pub name: nzfcc::NzfccCode,
pub groups: TransactionGroups,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct TransactionGroups {
pub personal_finance: PersonalFinanceGroup,
#[serde(flatten)]
pub other_groups: Option<std::collections::HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PersonalFinanceGroup {
#[serde(rename = "_id")]
pub id: CategoryId,
pub name: nzfcc::CategoryGroup,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct TransactionMerchant {
#[serde(rename = "_id")]
pub id: MerchantId,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub website: Option<url::Url>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct TransactionMeta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub particulars: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reference: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub other_account: Option<BankAccountNumber>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub conversion: Option<TransactionConversion>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub card_suffix: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logo: Option<url::Url>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct TransactionConversion {
#[serde(with = "rust_decimal::serde::arbitrary_precision")]
pub amount: rust_decimal::Decimal,
pub currency: iso_currency::Currency,
#[serde(with = "rust_decimal::serde::arbitrary_precision")]
pub rate: rust_decimal::Decimal,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PendingTransaction {
#[serde(rename = "_account")]
pub account: AccountId,
#[serde(rename = "_connection")]
pub connection: ConnectionId,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub date: chrono::DateTime<chrono::Utc>,
pub description: String,
#[serde(with = "rust_decimal::serde::arbitrary_precision")]
pub amount: rust_decimal::Decimal,
#[serde(rename = "type")]
pub kind: TransactionKind,
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
pub meta: Option<TransactionMeta>,
}