1use chrono::{DateTime, NaiveDate, Utc};
2use rust_decimal::Decimal;
3use serde::{Deserialize, Serialize, Serializer};
4use uuid::Uuid;
5
6pub fn serialize_amount<S>(amount: &i64, serializer: S) -> Result<S::Ok, S::Error>
8where
9 S: Serializer,
10{
11 let decimal_amount = Decimal::from(*amount) / Decimal::from(1000);
12 serializer.serialize_str(&decimal_amount.to_string())
13}
14
15pub fn serialize_amount_option<S>(amount: &Option<i64>, serializer: S) -> Result<S::Ok, S::Error>
17where
18 S: Serializer,
19{
20 match amount {
21 Some(amt) => {
22 let decimal_amount = Decimal::from(*amt) / Decimal::from(1000);
23 serializer.serialize_some(&decimal_amount.to_string())
24 }
25 None => serializer.serialize_none(),
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ApiResponse<T> {
31 pub data: T,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct BudgetsResponse {
36 pub budgets: Vec<Budget>,
37 pub default_budget: Option<Budget>,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct BudgetResponse {
42 pub budget: Budget,
43 pub server_knowledge: i64,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct UserResponse {
48 pub user: User,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct AccountsResponse {
53 pub accounts: Vec<Account>,
54 pub server_knowledge: i64,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct AccountResponse {
59 pub account: Account,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct CategoriesResponse {
64 pub category_groups: Vec<CategoryGroup>,
65 pub server_knowledge: i64,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct CategoryResponse {
70 pub category: Category,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct PayeesResponse {
75 pub payees: Vec<Payee>,
76 pub server_knowledge: i64,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct PayeeResponse {
81 pub payee: Payee,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct TransactionsResponse {
86 pub transactions: Vec<Transaction>,
87 pub server_knowledge: i64,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct TransactionResponse {
92 pub transaction: Transaction,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct ScheduledTransactionsResponse {
97 pub scheduled_transactions: Vec<ScheduledTransaction>,
98 pub server_knowledge: i64,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct ScheduledTransactionResponse {
103 pub scheduled_transaction: ScheduledTransaction,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct MonthsResponse {
108 pub months: Vec<Month>,
109 pub server_knowledge: i64,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct MonthResponse {
114 pub month: Month,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct Budget {
119 pub id: Uuid,
120 pub name: String,
121 pub last_modified_on: Option<DateTime<Utc>>, pub first_month: Option<NaiveDate>, pub last_month: Option<NaiveDate>, pub date_format: Option<DateFormat>, pub currency_format: Option<CurrencyFormat>, pub accounts: Option<Vec<Account>>,
127 pub payees: Option<Vec<Payee>>,
128 pub payee_locations: Option<Vec<PayeeLocation>>,
129 pub category_groups: Option<Vec<CategoryGroup>>,
130 pub categories: Option<Vec<Category>>,
131 pub months: Option<Vec<Month>>,
132 pub transactions: Option<Vec<Transaction>>,
133 pub subtransactions: Option<Vec<SubTransaction>>,
134 pub scheduled_transactions: Option<Vec<ScheduledTransaction>>,
135 pub scheduled_subtransactions: Option<Vec<ScheduledSubTransaction>>,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct DateFormat {
140 pub format: String,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct CurrencyFormat {
145 pub iso_code: String,
146 pub example_format: String,
147 pub decimal_digits: u32,
148 pub decimal_separator: String,
149 pub symbol_first: bool,
150 pub group_separator: String,
151 pub currency_symbol: String,
152 pub display_symbol: bool,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct Account {
157 pub id: Uuid,
158 pub name: String,
159 #[serde(rename = "type")]
160 pub account_type: AccountType,
161 pub on_budget: bool,
162 pub closed: bool,
163 pub note: Option<String>,
164 #[serde(serialize_with = "serialize_amount")]
165 pub balance: i64, #[serde(serialize_with = "serialize_amount")]
167 pub cleared_balance: i64, #[serde(serialize_with = "serialize_amount")]
169 pub uncleared_balance: i64, pub transfer_payee_id: Option<Uuid>, pub direct_import_linked: Option<bool>, pub direct_import_in_error: Option<bool>, pub last_reconciled_at: Option<DateTime<Utc>>,
174 #[serde(serialize_with = "serialize_amount_option")]
175 pub debt_original_balance: Option<i64>,
176 pub debt_interest_rates: Option<serde_json::Value>, pub debt_minimum_payments: Option<serde_json::Value>, pub debt_escrow_amounts: Option<serde_json::Value>, pub deleted: bool,
180}
181
182#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183pub enum AccountType {
184 #[serde(rename = "checking")]
185 Checking,
186 #[serde(rename = "savings")]
187 Savings,
188 #[serde(rename = "creditCard")]
189 CreditCard,
190 #[serde(rename = "cash")]
191 Cash,
192 #[serde(rename = "lineOfCredit")]
193 LineOfCredit,
194 #[serde(rename = "otherAsset")]
195 OtherAsset,
196 #[serde(rename = "otherLiability")]
197 OtherLiability,
198 #[serde(rename = "mortgage")]
199 Mortgage,
200 #[serde(rename = "autoLoan")]
201 AutoLoan,
202 #[serde(rename = "studentLoan")]
203 StudentLoan,
204 #[serde(rename = "personalLoan")]
205 PersonalLoan,
206 #[serde(rename = "medicalDebt")]
207 MedicalDebt,
208 #[serde(rename = "otherDebt")]
209 OtherDebt,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct Payee {
214 pub id: Uuid,
215 pub name: String,
216 pub transfer_account_id: Option<Uuid>,
217 pub deleted: bool,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct PayeeLocation {
222 pub id: Uuid,
223 pub payee_id: Uuid,
224 pub latitude: String, pub longitude: String, pub deleted: bool,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct CategoryGroup {
231 pub id: Uuid,
232 pub name: String,
233 pub hidden: bool,
234 pub deleted: bool,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct Category {
239 pub id: Uuid,
240 pub category_group_id: Uuid,
241 pub category_group_name: Option<String>,
242 pub name: String,
243 pub hidden: bool,
244 pub original_category_group_id: Option<Uuid>,
245 pub note: Option<String>,
246 #[serde(serialize_with = "serialize_amount")]
247 pub budgeted: i64, #[serde(serialize_with = "serialize_amount")]
249 pub activity: i64, #[serde(serialize_with = "serialize_amount")]
251 pub balance: i64, pub goal_type: Option<String>, pub goal_needs_whole_amount: Option<bool>,
254 pub goal_day: Option<u32>,
255 pub goal_cadence: Option<u32>,
256 pub goal_cadence_frequency: Option<u32>,
257 pub goal_creation_month: Option<String>, pub goal_target: Option<i64>,
259 pub goal_target_month: Option<String>, pub goal_percentage_complete: Option<u32>,
261 pub goal_months_to_budget: Option<u32>,
262 pub goal_under_funded: Option<i64>,
263 pub goal_overall_funded: Option<i64>,
264 pub goal_overall_left: Option<i64>,
265 pub goal_snoozed_at: Option<String>, pub deleted: bool,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct Month {
271 pub month: NaiveDate,
272 pub note: Option<String>,
273 #[serde(serialize_with = "serialize_amount")]
274 pub income: i64, #[serde(serialize_with = "serialize_amount")]
276 pub budgeted: i64, #[serde(serialize_with = "serialize_amount")]
278 pub activity: i64, #[serde(serialize_with = "serialize_amount")]
280 pub to_be_budgeted: i64, pub age_of_money: Option<u32>,
282 pub deleted: bool,
283 pub categories: Option<Vec<Category>>, }
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct Transaction {
288 pub id: Uuid,
289 pub date: NaiveDate,
290 #[serde(serialize_with = "serialize_amount")]
291 pub amount: i64, pub memo: Option<String>,
293 pub cleared: ClearState,
294 pub approved: bool,
295 pub flag_color: Option<String>,
296 pub flag_name: Option<String>,
297 pub account_id: Uuid,
298 pub payee_id: Option<Uuid>,
299 pub category_id: Option<Uuid>,
300 pub transfer_account_id: Option<Uuid>,
301 pub transfer_transaction_id: Option<Uuid>,
302 pub matched_transaction_id: Option<Uuid>,
303 pub import_id: Option<String>,
304 pub import_payee_name: Option<String>,
305 pub import_payee_name_original: Option<String>,
306 pub debt_transaction_type: Option<String>,
307 pub deleted: bool,
308 pub account_name: String,
309 pub payee_name: Option<String>,
310 pub category_name: Option<String>,
311 pub subtransactions: Option<Vec<SubTransaction>>, #[serde(rename = "type")]
314 pub transaction_type: Option<String>,
315 pub parent_transaction_id: Option<Uuid>,
316}
317
318#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
319pub enum ClearState {
320 #[serde(rename = "cleared")]
321 Cleared,
322 #[serde(rename = "uncleared")]
323 Uncleared,
324 #[serde(rename = "reconciled")]
325 Reconciled,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
329pub struct SubTransaction {
330 pub id: Uuid,
331 pub transaction_id: Uuid,
332 #[serde(serialize_with = "serialize_amount")]
333 pub amount: i64, pub memo: Option<String>,
335 pub payee_id: Option<Uuid>,
336 pub payee_name: Option<String>,
337 pub category_id: Option<Uuid>,
338 pub category_name: Option<String>,
339 pub transfer_account_id: Option<Uuid>,
340 pub transfer_transaction_id: Option<Uuid>,
341 pub deleted: bool,
342}
343
344impl Transaction {
345 pub fn fix_amount_if_needed(&mut self) {
347 if let Some(import_id) = &self.import_id {
348 let parts: Vec<&str> = import_id.split(':').collect();
350 if parts.len() >= 2 && parts[0] == "YNAB" {
351 if let Ok(expected_milliunits) = parts[1].parse::<i64>() {
352 let current_abs = self.amount.abs();
354 let expected_abs = expected_milliunits.abs();
355
356 if expected_abs >= 1000 && current_abs < 100 {
358 self.amount = expected_milliunits;
361 }
362 }
363 }
364 }
365 }
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct ScheduledTransaction {
370 pub id: Uuid,
371 pub date_first: NaiveDate,
372 pub date_next: NaiveDate, pub frequency: String, #[serde(serialize_with = "serialize_amount")]
375 pub amount: i64, pub memo: Option<String>,
377 pub flag_color: Option<String>,
378 pub flag_name: Option<String>,
379 pub account_id: Uuid,
380 pub payee_id: Option<Uuid>,
381 pub category_id: Option<Uuid>,
382 pub transfer_account_id: Option<Uuid>,
383 pub deleted: bool,
384 pub account_name: String, pub payee_name: Option<String>, pub category_name: Option<String>, }
388
389#[derive(Debug, Clone, Serialize, Deserialize)]
390pub struct ScheduledSubTransaction {
391 pub id: Uuid,
392 pub scheduled_transaction_id: Uuid,
393 #[serde(serialize_with = "serialize_amount")]
394 pub amount: i64, pub memo: Option<String>,
396 pub payee_id: Option<Uuid>,
397 pub category_id: Option<Uuid>,
398 pub transfer_account_id: Option<Uuid>,
399 pub deleted: bool,
400 pub payee_name: Option<String>,
401 pub category_name: Option<String>,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct User {
406 pub id: Uuid,
407}
408
409#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct TransactionDetail {
411 pub account_id: Uuid,
412 pub payee_id: Option<Uuid>,
413 pub category_id: Option<Uuid>,
414 pub amount: i64, pub memo: Option<String>,
416 pub cleared: Option<ClearState>,
417 pub approved: Option<bool>,
418 pub flag_color: Option<String>,
419 pub flag_name: Option<String>,
420 pub date: NaiveDate,
421 pub import_id: Option<String>,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct NewTransaction {
426 pub transactions: Vec<TransactionDetail>,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct UpdateTransaction {
431 pub transaction: TransactionDetail,
432}