firefly_iii_rust/
transaction.rs

1use serde::{Serialize,Deserialize};
2use std::borrow::Cow;
3use crate::http::Method;
4use crate::requests::Request;
5use crate::response::*;
6
7#[derive(Deserialize, Default, Debug, Clone)]
8pub struct Data {
9    pub r#type: String,
10    pub id: String,
11    pub attributes: Attributes,
12}
13
14#[derive(Deserialize, Default, Debug, Clone)]
15pub struct Attributes {
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub created_at: Option<String>,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub updated_at: Option<String>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub user: Option<String>,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub group_title: Option<String>,
24    pub transactions: Vec<Transaction>,
25}
26
27#[derive(Deserialize, Default, Debug, Clone)]
28pub struct Transaction {
29    pub amount: String,
30    pub date: String,
31    pub r#type: Type,
32    pub description: String,
33    pub source_id: String,
34    pub destination_id: String,
35    
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub interest_date: Option<String>,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub book_date: Option<String>,
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub process_date: Option<String>,
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub due_date: Option<String>,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub invoice_date: Option<String>,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub payment_date: Option<String>,
48
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub currency_id: Option<String>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub currency_code: Option<String>,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub currency_symbol: Option<String>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub currency_name: Option<String>,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub currency_decimal_places: Option<u32>,
59
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub foreign_currency_id: Option<String>,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub foreign_currency_code: Option<String>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub foreign_currency_symbol: Option<String>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub foreign_currency_name: Option<String>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub foreign_currency_decimal_places: Option<u32>,
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub foreign_amount: Option<String>,
72
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub source_name: Option<String>,
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub source_iban: Option<String>,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub source_type: Option<String>,
79
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub destination_name: Option<String>,
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub destination_iban: Option<String>,
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub destination_type: Option<String>,
86
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub budget_id: Option<String>,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub budget_name: Option<String>,
91
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub category_id: Option<String>,
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub category_name: Option<String>,
96
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub bill_id: Option<String>,
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub bill_name: Option<String>,
101
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub reconciled: Option<bool>,
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub notes: Option<String>,
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub tags: Option<Vec<String>>,
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub internal_reference: Option<String>,
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub external_id: Option<String>,
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub external_url: Option<String>,
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub original_source: Option<String>,
116
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub recurrence_id: Option<String>,
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub recurrence_total: Option<u32>,
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub recurrence_count: Option<u32>,
123
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub sepa_cc: Option<String>,
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub sepa_ct_op: Option<String>,
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub sepa_ct_id: Option<String>,
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub sepa_db: Option<String>,
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub sepa_country: Option<String>,
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub sepa_ep: Option<String>,
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub sepa_ci: Option<String>,
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub sepa_batch_id: Option<String>,
140
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub user: Option<String>,
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub order: Option<u32>,
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub transaction_journal_id: Option<String>,
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub import_hash_v2: Option<String>,
149
150    #[serde(skip_serializing_if = "Option::is_none")]
151    pub latitude: Option<f64>,
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub longitude: Option<f64>,
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub zoom_level: Option<u32>,
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub has_attachments: Option<bool>,
158}
159
160#[derive(Serialize, Deserialize, Clone, Default, Debug)]
161#[serde(rename_all = "lowercase")]
162pub enum Type {
163    #[default]
164    Withdrawal,
165    Deposit,
166    Transfer,
167    Reconciliation,
168    OpeningBalance,
169}
170
171#[derive(Serialize, Default, Deserialize, Clone, Debug)]
172pub struct Create {
173    pub group_title: Option<String>,
174    pub transactions: Vec<CreateTransaction>,
175}
176
177#[derive(Serialize, Default, Deserialize, Clone, Debug)]
178pub struct CreateTransaction {
179    pub r#type: Type,
180    pub date: String,
181    pub amount: String,
182    pub description: String,
183
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub source_id: Option<String>,
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub source_name: Option<String>,
188    #[serde(skip_serializing_if = "Option::is_none")]
189    pub destination_id: Option<String>,
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub destination_name: Option<String>,
192
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub sepa_cc: Option<String>,
195    #[serde(skip_serializing_if = "Option::is_none")]
196    pub sepa_ct_op: Option<String>,
197    #[serde(skip_serializing_if = "Option::is_none")]
198    pub sepa_ct_id: Option<String>,
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub sepa_db: Option<String>,
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub sepa_country: Option<String>,
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub sepa_ep: Option<String>,
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub sepa_ci: Option<String>,
207    #[serde(skip_serializing_if = "Option::is_none")]
208    pub sepa_batch_id: Option<String>,
209
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub latitude: Option<f64>,
212    #[serde(skip_serializing_if = "Option::is_none")]
213    pub longitude: Option<f64>,
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub zoom_level: Option<u32>,
216    #[serde(skip_serializing_if = "Option::is_none")]
217    pub has_attachments: Option<bool>,
218}
219
220impl Request for Create {
221    type Body = Create;
222    type Response = Response<Data>;
223    const METHOD: Method = Method::Post;
224    fn endpoint(&self) -> Cow<str> {
225        format!("/v1/transactions").into()
226    }
227    fn body(&self) -> Option<Self::Body> {
228        Some(self.clone())
229    }
230}
231
232#[derive(Serialize, Default, Deserialize, Clone, Debug)]
233pub struct Get {
234    pub id: String,
235}
236
237impl Request for Get {
238    type Body = ();
239    type Response = Response<Data>;
240    const METHOD: Method = Method::Get;
241    fn endpoint(&self) -> Cow<str> {
242        format!("/v1/transactions/{}", self.id).into()
243    }
244}
245
246#[derive(Serialize, Deserialize, Clone, Debug)]
247pub struct Delete {
248    pub id: String,
249}
250
251impl Request for Delete {
252    type Body = ();
253    type Response = EmptyResponse;
254    const METHOD: Method = Method::Delete;
255    fn endpoint(&self) -> Cow<str> {
256        format!("/v1/transactions/{}", self.id).into()
257    }
258}
259
260#[derive(Serialize, Default, Deserialize, Clone, Debug)]
261pub struct List {
262    pub current_page: u64,
263    pub total_pages: u64,
264}
265
266impl Request for List {
267    type Body = ();
268    type Response = PaginatedResponse<Vec<Data>>;
269    const METHOD: Method = Method::Get;
270    fn endpoint(&self) -> Cow<str> {
271        let mut endpoint = "/v1/transactions".to_string();
272        endpoint.push_str("?");
273        endpoint.push_str(&format!("page={}", self.current_page));
274        endpoint.push_str("&");
275        endpoint.push_str("limit=50");
276        endpoint.into()
277    }
278}
279
280impl Paginated for List {
281    fn set_page(&mut self, page: u64) {
282        self.current_page = page;
283    }
284
285    fn get_page(&self) -> u64 {
286        self.current_page
287    }
288
289    fn max_page(&self) -> u64 {
290        self.total_pages
291    }
292}
293