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