1use crate::models::cards::CardType;
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
10#[serde(rename_all = "lowercase")]
11pub enum TransactionType {
12 Spend,
13 Collateral,
14 Payment,
15 Fee,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
20#[serde(rename_all = "lowercase")]
21pub enum SpendTransactionStatus {
22 Pending,
23 Reversed,
24 Declined,
25 Completed,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
30#[serde(rename_all = "lowercase")]
31pub enum PaymentTransactionStatus {
32 Pending,
33 Completed,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(rename_all = "camelCase")]
39pub struct SpendTransaction {
40 pub amount: i64,
41 pub currency: String,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub local_amount: Option<i64>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub local_currency: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub authorized_amount: Option<i64>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub authorization_method: Option<String>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub memo: Option<String>,
52 pub receipt: bool,
53 pub merchant_name: String,
54 pub merchant_category: String,
55 pub merchant_category_code: String,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub merchant_id: Option<String>,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub enriched_merchant_icon: Option<String>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub enriched_merchant_name: Option<String>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub enriched_merchant_category: Option<String>,
64 pub card_id: Uuid,
65 pub card_type: CardType,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub company_id: Option<Uuid>,
68 pub user_id: Uuid,
69 pub user_first_name: String,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub user_last_name: Option<String>,
72 pub user_email: String,
73 pub status: SpendTransactionStatus,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub declined_reason: Option<String>,
76 pub authorized_at: String,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub posted_at: Option<String>,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(rename_all = "camelCase")]
84pub struct CollateralTransaction {
85 pub amount: f64,
86 pub currency: String,
87 pub chain_id: i64,
88 pub wallet_address: String,
89 pub transaction_hash: String,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub memo: Option<String>,
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub company_id: Option<Uuid>,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub user_id: Option<Uuid>,
96 #[serde(skip_serializing_if = "Option::is_none")]
97 pub posted_at: Option<DateTime<Utc>>,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct PaymentTransaction {
104 pub amount: i64,
105 pub currency: String,
106 pub status: PaymentTransactionStatus,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub memo: Option<String>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub chain_id: Option<i64>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub wallet_address: Option<String>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub transaction_hash: Option<String>,
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub company_id: Option<Uuid>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub user_id: Option<Uuid>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub posted_at: Option<String>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub struct FeeTransaction {
127 pub amount: i64,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub description: Option<String>,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub company_id: Option<Uuid>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub user_id: Option<Uuid>,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub posted_at: Option<DateTime<Utc>>,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
140#[serde(tag = "type")]
141#[allow(clippy::large_enum_variant)]
142pub enum Transaction {
143 #[serde(rename = "spend")]
144 Spend {
145 id: Uuid,
146 #[serde(flatten)]
147 spend: SpendTransaction,
148 },
149 #[serde(rename = "collateral")]
150 Collateral {
151 id: Uuid,
152 #[serde(flatten)]
153 collateral: CollateralTransaction,
154 },
155 #[serde(rename = "payment")]
156 Payment {
157 id: Uuid,
158 #[serde(flatten)]
159 payment: PaymentTransaction,
160 },
161 #[serde(rename = "fee")]
162 Fee {
163 id: Uuid,
164 #[serde(flatten)]
165 fee: FeeTransaction,
166 },
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171#[serde(rename_all = "camelCase")]
172pub struct ListTransactionsParams {
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub company_id: Option<Uuid>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub user_id: Option<Uuid>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub card_id: Option<Uuid>,
179 #[serde(
180 rename = "type",
181 skip_serializing_if = "Option::is_none",
182 serialize_with = "serialize_transaction_types"
183 )]
184 pub transaction_type: Option<Vec<TransactionType>>,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub transaction_hash: Option<String>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub authorized_before: Option<DateTime<Utc>>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub authorized_after: Option<DateTime<Utc>>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub posted_before: Option<DateTime<Utc>>,
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub posted_after: Option<DateTime<Utc>>,
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub cursor: Option<String>,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub limit: Option<u32>,
199}
200
201fn serialize_transaction_types<S>(
202 types: &Option<Vec<TransactionType>>,
203 serializer: S,
204) -> Result<S::Ok, S::Error>
205where
206 S: serde::Serializer,
207{
208 use serde::ser::SerializeSeq;
209
210 match types {
211 Some(ref vec) => {
212 let mut seq = serializer.serialize_seq(Some(vec.len()))?;
213 for item in vec {
214 let s = match item {
215 TransactionType::Spend => "spend",
216 TransactionType::Collateral => "collateral",
217 TransactionType::Payment => "payment",
218 TransactionType::Fee => "fee",
219 };
220 seq.serialize_element(s)?;
221 }
222 seq.end()
223 }
224 None => serializer.serialize_none(),
225 }
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230#[serde(rename_all = "camelCase")]
231pub struct UpdateTransactionRequest {
232 #[serde(skip_serializing_if = "Option::is_none")]
233 pub memo: Option<String>,
234}
235
236#[derive(Debug, Clone)]
238pub struct UploadReceiptRequest {
239 pub receipt: Vec<u8>,
240 pub file_name: String,
241}
242
243pub type ListTransactionsResponse = Vec<Transaction>;