1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
//! Data model. All structs and attributes coresponds with official API
//! [documentation](https://fakturoid.docs.apiary.io)

use chrono::{DateTime, Local, NaiveDate};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::Read;
use std::path::Path;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VatMode {
    VatPayer,
    NonVatPayer,
    IdentifiedPerson
}

#[derive(Debug, Deserialize)]
pub struct Account {
    pub subdomain: String,
    pub plan: String,
    pub plan_price: i32,
    pub email: String,
    pub invoice_email: Option<String>,
    pub phone: Option<String>,
    pub web: Option<String>,
    pub name: String,
    pub full_name: Option<String>,
    pub registration_no: Option<String>,
    pub vat_no: Option<String>,
    pub vat_mode: VatMode,
    pub vat_price_mode: VatPriceMode,
    pub street: String,
    pub street2: Option<String>,
    pub city: String,
    pub zip: String,
    pub country: String,
    pub bank_account: String,
    pub iban: Option<String>,
    pub swift_bic: Option<String>,
    pub currency: String,
    pub unit_name: Option<String>,
    pub vat_rate: i32,
    pub displayed_note: Option<String>,
    pub invoice_note: Option<String>,
    pub due: i32,
    pub custom_email_text: String,
    pub overdue_email_text: String,
    pub invoice_paypal: bool,
    pub invoice_gopay: bool,
    pub html_url: String,
    pub url: String,
    pub created_at: DateTime<Local>,
    pub updated_at: DateTime<Local>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SubjectType {
    Customer,
    Supplier,
    Both,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Subject {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub custom_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sub_type: Option<SubjectType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub street: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub street2: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub city: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub zip: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub country: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub registration_no: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vat_no: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub local_vat_no: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bank_account: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub iban: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub variable_symbol: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub enabled_reminders: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub full_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email_copy: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phone: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub web: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub private_note: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub html_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub updated_at: Option<DateTime<Local>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum InvoiceState {
    Open,
    Sent,
    Overdue,
    Paid,
    Cancelled,
}

impl ToString for InvoiceState {
    fn to_string(&self) -> String {
        match self {
            InvoiceState::Open => "open".to_string(),
            InvoiceState::Sent => "sent".to_string(),
            InvoiceState::Overdue => "overdue".to_string(),
            InvoiceState::Paid => "paid".to_string(),
            InvoiceState::Cancelled => "cancelled".to_string(),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PaymentMethod {
    Bank,
    Cash,
    Cod,
    Paypal,
    Card,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum InvoiceLanguage {
    Cz,
    Sk,
    En,
    De,
    Fr,
    It,
    Es,
    Ru,
    Hu,
    Pl,
    Ro,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VatPriceMode {
    WithoutVat,
    FromTotalWithVat,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EetStatus {
    Waiting,
    Pkp,
    Fik,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EetRecord {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<i32>,
    pub vat_no: String,
    pub number: String,
    pub store: i32,
    pub cash_register: String,
    pub paid_at: DateTime<Local>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vat_base0: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vat_base1: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vat1: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vat_base2: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vat2: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vat_base3: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vat3: Option<Decimal>,
    pub total: Decimal,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fik: Option<String>,
    pub bkp: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pkp: Option<String>,
    pub status: EetStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fik_received_at: Option<DateTime<Local>>,
    pub external: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attempts: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub last_attempt_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub last_uuid: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub playground: Option<bool>,
    pub invoice_id: i32,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub updated_at: Option<DateTime<Local>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemoteAttachment {
    file_name: String,
    content_type: String,
    download_url: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum Attachment {
    Update(String),
    Received(RemoteAttachment),
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Invoice {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub custom_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub proforma: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub partial_proforma: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub number: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub variable_symbol: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub your_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub your_street: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub your_street2: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub your_city: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub your_zip: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub your_country: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub your_registration_no: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub your_vat_no: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub your_local_vat_no: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_street: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_street2: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_city: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_zip: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_country: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_registration_no: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_vat_no: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_local_vat_no: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subject_id: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subject_custom_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub generator_id: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub related_id: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub correction: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub correction_id: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub token: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<InvoiceState>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_number: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub issued_on: Option<NaiveDate>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub taxable_fulfillment_due: Option<NaiveDate>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub due: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub due_on: Option<NaiveDate>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sent_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub paid_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reminder_sent_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub accepted_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cancelled_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub note: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub footer_note: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub private_note: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bank_account_id: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bank_account: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub iban: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub swift_bic: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method: Option<PaymentMethod>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub currency: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub exchange_rate: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub paypal: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub gopay: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub language: Option<InvoiceLanguage>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transferred_tax_liability: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub supply_code: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub eu_electronic_service: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vat_price_mode: Option<VatPriceMode>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub round_total: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subtotal: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub native_subtotal: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub total: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub native_total: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub remaining_amount: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub remaining_native_amount: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub paid_amount: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub eet: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub eet_cash_register: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub eet_store: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub eet_records: Option<Vec<EetRecord>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    attachment: Option<Attachment>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub html_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub public_html_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pdf_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subject_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub updated_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lines: Option<Vec<InvoiceLine>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct InvoiceLine {
    pub id: Option<i32>,
    pub name: String,
    pub quantity: Decimal,
    pub unit_name: Option<String>,
    pub unit_price: Decimal,
    pub vat_rate: i32,
    pub unit_price_without_vat: Option<Decimal>,
    pub unit_price_with_vat: Option<Decimal>,
}

impl InvoiceLine {
    pub fn new(
        name: &str,
        quantity: Decimal,
        unit_name: Option<&str>,
        unit_price: Decimal,
        vat_rate: i32,
    ) -> Self {
        Self {
            id: None,
            name: name.to_string(),
            quantity,
            unit_name: unit_name.map(|n| n.to_string()),
            unit_price,
            vat_rate,
            unit_price_without_vat: None,
            unit_price_with_vat: None,
        }
    }
}

impl Invoice {
    pub fn set_attachment(&mut self, path: &Path) -> Result<(), ()> {
        if path.is_file() {
            let mut file = File::open(path).map_err(|_| ())?;
            let mut file_content: Vec<u8> = Vec::new();
            file.read_to_end(&mut file_content).map_err(|_| ())?;
            self.attachment = Some(Attachment::Update(format!(
                "data:{};base64,{}",
                tree_magic::from_u8(&file_content),
                base64::encode_config(file_content, base64::STANDARD_NO_PAD)
            )));
            return Ok(());
        }
        Err(())
    }

    pub fn attachment(&self) -> Option<&RemoteAttachment> {
        if let Some(attachment) = self.attachment.as_ref() {
            if let Attachment::Received(rcv) = attachment {
                Some(rcv)
            } else {
                None
            }
        } else {
            None
        }
    }
}

#[derive(Serialize)]
pub struct InvoicePayData {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub paid_at: Option<DateTime<Local>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub paid_amount: Option<Decimal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub variable_symbol: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bank_account_id: Option<i32>
}

pub enum InvoiceAction {
    MarkAsSent,
    Deliver,
    Pay,
    PayProforma,
    PayPartialProforma,
    RemovePayment,
    DeliverReminder,
    Cancel,
    UndoCancel,
    Lock,
    Unlock
}

impl ToString for InvoiceAction {
    fn to_string(&self) -> String {
        match self {
            InvoiceAction::MarkAsSent => { "mark_as_sent" }
            InvoiceAction::Deliver => { "deliver" }
            InvoiceAction::Pay => { "pay" }
            InvoiceAction::PayProforma => { "pay_proforma" }
            InvoiceAction::PayPartialProforma => { "pay_partial_proforma" }
            InvoiceAction::RemovePayment => { "remove_payment" }
            InvoiceAction::DeliverReminder => { "deliver_reminder" }
            InvoiceAction::Cancel => { "cancel" }
            InvoiceAction::UndoCancel => { "undo_cancel" }
            InvoiceAction::Lock => { "lock" }
            InvoiceAction::Unlock => { "unlock" }
        }.to_string()
    }
}