bai2 0.4.0

A tool to parse BAI2 files
Documentation
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
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
use chrono::NaiveDate;
use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize;
use std::collections::HashMap;

use crate::scanner::node::Node;

use super::funds_type::{FundsSubType, FundsType};
use super::transaction::Transaction;
use super::util::{parse_currency, parse_date, parse_int, parse_string, parse_time};

#[derive(Debug, Serialize)]
pub struct Account {
    amounts: Vec<Amount>,
    currency_code: String,
    customer_account_number: String,
    transactions: Vec<Transaction>,
    value_date: Option<NaiveDate>,
    value_time: Option<String>,
}

impl Account {
    pub fn from_node(node: &Node, default_currency: &str) -> Result<Account, &'static str> {
        let header_fields = node.fields();
        if header_fields.len() < 7 {
            return Err("Invalid account header. Expected 7 fields, but found less.");
        }

        let trailer_fields = node.sibling_fields();
        if trailer_fields.len() < 3 {
            return Err("Invalid account trailer. Expected 3 fields, but found less.");
        }

        let txns_result = node
            .children
            .iter()
            .map(Transaction::from_node)
            .collect::<Result<Vec<Transaction>, &'static str>>();

        match txns_result {
            Err(e) => Err(e),
            Ok(transactions) => Ok(Account {
                amounts: Amount::parse(header_fields[3..].to_vec()),
                currency_code: parse_currency(header_fields[2], default_currency),
                customer_account_number: parse_string(header_fields[1]),
                transactions,
                value_date: None,
                value_time: None,
            }),
        }
    }
}

#[derive(Debug, Serialize)]
pub struct Amount {
    amount_type: AmountType,
    amount: Option<i64>,
    availability: HashMap<u16, i64>,
    funds_type: FundsType,
    item_count: Option<u16>,
    value_date: Option<NaiveDate>,
    value_time: Option<String>,
}

impl Amount {
    fn parse(fields: Vec<&str>) -> Vec<Amount> {
        let mut amounts = Vec::new();
        let mut next_start_index = 0;

        while fields.len() > next_start_index + 1 {
            let mut amount = Amount {
                amount: parse_int(fields[next_start_index + 1]),
                amount_type: AmountType::parse(fields[next_start_index]),
                availability: HashMap::new(),
                funds_type: FundsType::parse(fields[next_start_index + 3]),
                item_count: parse_int(fields[next_start_index + 2]),
                value_date: None,
                value_time: None,
            };

            match amount.funds_type {
                FundsType::ValueDated => {
                    amount.value_date = parse_date(fields[next_start_index + 4]);
                    amount.value_time = parse_time(fields[next_start_index + 5]);
                    next_start_index = next_start_index + 6;
                }
                FundsType::DistributedAvailability(FundsSubType::S) => {
                    amount
                        .availability
                        .insert(0, parse_int(fields[next_start_index + 4]).unwrap());
                    amount
                        .availability
                        .insert(1, parse_int(fields[next_start_index + 5]).unwrap());
                    amount
                        .availability
                        .insert(2, parse_int(fields[next_start_index + 6]).unwrap());
                    next_start_index = next_start_index + 7;
                }
                FundsType::DistributedAvailability(FundsSubType::D) => {
                    let num_distributions = parse_int(fields[next_start_index + 4]).unwrap_or(0);
                    next_start_index = next_start_index + 5;

                    for _ in 0..num_distributions {
                        match (
                            parse_int(fields[next_start_index]),
                            parse_int(fields[next_start_index + 1]),
                        ) {
                            (Some(days), Some(amt)) => {
                                amount.availability.insert(days, amt);
                            }
                            _ => {}
                        }

                        next_start_index = next_start_index + 2;
                    }
                }
                _ => {
                    next_start_index = next_start_index + 4;
                }
            }

            amounts.push(amount);
        }

        return amounts;
    }
}

#[derive(Debug)]
pub enum AmountType {
    Status(String, AmountSubtype),
    CreditSummary(String, AmountSubtype),
    DebitSummary(String, AmountSubtype),
    Unknown(String, AmountSubtype),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AmountSubtype {
    AchNetPosition,
    AchSettlementCredits,
    AchSettlementDebits,
    AdjustedBalance,
    AdjustedBalanceMtd,
    AdjustedBalanceYtd,
    AdjustedTotalDisbursement,
    AdjustmentToBalances,
    AggregateBalanceAdjustments,
    AvailableCommitmentAmount,
    Average1DayFloatMtd,
    Average1DayFloatYtd,
    Average2DayFloatMtd,
    Average2DayFloatYtd,
    AverageAdjustmentToBalancesMtd,
    AverageAdjustmentToBalancesYtd,
    AverageAvailablePreviousMonth,
    AverageClosingAvailableLastMonth,
    AverageClosingAvailableMtd,
    AverageClosingAvailableYtd,
    AverageClosingAvailableYtdLastMonth,
    AverageClosingLedgerMtd,
    AverageClosingLedgerPreviousMonth,
    AverageClosingLedgerYtd,
    AverageClosingLedgerYtdPreviousMonth,
    AverageCurrentAvailableMtd,
    AverageCurrentAvailableYtd,
    AverageOpeningAvailableMtd,
    AverageOpeningAvailableYtd,
    AverageOpeningLedgerMtd,
    AverageOpeningLedgerYtd,
    ClosingAvailable,
    ClosingLedger,
    CorporateTradePaymentCredits,
    CorporateTradePaymentDebits,
    CorporateTradePaymentSettlement,
    CorrespondentBankDeposit,
    CreditsNotDetailed,
    CurrentAvailable,
    CurrentAvailableCrsSupressed,
    CurrentDayTotalLockboxDeposits,
    CurrentLedger,
    CustomCreditSummary,
    CustomDebitSummary,
    CustomStatus,
    DebitsNotDetailed,
    DepositsSubjectToFloat,
    DisbursingFundingRequirement,
    DisbursingOpeningAvailableBalance,
    EdiTransactionCredit,
    EdiTransactionDebits,
    EstimatedTotalDisbursement,
    FiveDayFloat,
    FloatAdjustment,
    FourDayFloat,
    FrbFreightPaymentDebits,
    FrbPresentmentEstimate,
    GrandTotalCreditsLessGrandTotalDebits,
    InterceptDebits,
    InterestAmountPastDue,
    InvestmentInterest,
    InvestmentSold,
    InvestmentsPurchased,
    LateDebitsAfterNotification,
    LateDeposit,
    ListPostCredits,
    ListPostDebits,
    LoanBalance,
    LoanDisbursement,
    MonthlyDividends,
    NetZeroBalanceAmount,
    OneDayFloat,
    OpeningAvailable,
    OpeningAvailableAndTotalSameDayAchDtcDeposit,
    OpeningLedger,
    PaymentAmountDue,
    PrincipalAmountPastDue,
    PrincipalLoanBalance,
    SixDayFloat,
    TargetBalance,
    ThreeOrMoreDaysFloat,
    TodaysTotalDebits,
    TotalAchCredits,
    TotalAchDebits,
    TotalAchDisbursementFundingDebits,
    TotalAchDisbursingFundingCredits,
    TotalAchReturnItems,
    TotalAdjustmentCreditsYtd,
    TotalAmountOfSecuritiesPurchased,
    TotalAprDebits,
    TotalAtmCredits,
    TotalAtmDebits,
    TotalAutomaticTransferCredits,
    TotalAutomaticTransferDebits,
    TotalBackValueCredits,
    TotalBackValueDebits,
    TotalBankCardDeposits,
    TotalBankersAcceptanceCredits,
    TotalBankersAcceptancesDebit,
    TotalBankOriginatedDebits,
    TotalBankPreparedDeposits,
    TotalBookTransferCredits,
    TotalBookTransferDebits,
    TotalBrokerDebits,
    TotalBrokerDebitsChf,
    TotalBrokerDebitsFf,
    TotalBrokerDeposits,
    TotalBrokerDepositsChf,
    TotalBrokerDepositsFf,
    TotalCashCenterCredits,
    TotalCashCenterDebits,
    TotalCashLetterAdjustments,
    TotalCashLetterCredits,
    TotalCashLetterDebits,
    TotalCheckPaid,
    TotalCheckPaidCumulativeMtd,
    TotalChecksPostedAndReturned,
    TotalCollectionCredits,
    TotalCollectionDebit,
    TotalCommercialDeposits,
    TotalConcentrationCredits,
    TotalControlledDisbursingCredits,
    TotalControlledDisbursingDebits,
    TotalCreditAdjustment,
    TotalCreditAmountMtd,
    TotalCreditReversals,
    TotalCredits,
    TotalCreditsLessWireTransferAndReturnedChecks,
    TotalDebitAdjustments,
    TotalDebitAmountMtd,
    TotalDebitLessWireTransfersAndChargeBacks,
    TotalDebitReversals,
    TotalDebits,
    TotalDebitsExcludingReturnedItems,
    TotalDepositedItemsReturned,
    TotalDisbursingChecksPaidEarlyAmount,
    TotalDisbursingChecksPaidLastAmount,
    TotalDisbursingChecksPaidLaterAmount,
    TotalDtcCredits,
    TotalDtcDebits,
    TotalDtcDisbursingCredits,
    TotalEscrowCredits,
    TotalEscrowDebits,
    TotalFederalReserveBankCommercialBankDebit,
    TotalFedFundsPurchased,
    TotalFedFundsSold,
    TotalFloat,
    TotalForeignCheckPurchased,
    TotalFreightPaymentCredits,
    TotalFundsRequired,
    TotalIncomingMoneyTransfers,
    TotalInternationalCredits,
    TotalInternationalCreditsChf,
    TotalInternationalCreditsFf,
    TotalInternationalDebitChf,
    TotalInternationalDebitFf,
    TotalInternationalDebits,
    TotalInternationalMoneyTransferCredits,
    TotalInternationalMoneyTransferDebits,
    TotalInvestmentInterestDebits,
    TotalInvestmentPosition,
    TotalLettersOfCredit,
    TotalLoanPayment,
    TotalLoanPayments,
    TotalLoanProceeds,
    TotalLockboxDebits,
    TotalLockboxDeposits,
    TotalMiscellaneousCredits,
    TotalMiscellaneousDebits,
    TotalMiscellaneousDeposits,
    TotalMiscellaneousSecuritiesCreditsChf,
    TotalMiscellaneousSecuritiesCreditsFf,
    TotalMiscellaneousSecuritiesDbFf,
    TotalMiscellaneousSecuritiesDebitChf,
    TotalOtherCheckDeposits,
    TotalOutgoingMoneyTransfers,
    TotalPayableThroughDrafts,
    TotalPreauthorizedPaymentCredits,
    TotalRejectedCredits,
    TotalRejectedDebits,
    TotalSecuritiesInterest,
    TotalSecuritiesInterestChf,
    TotalSecuritiesInterestFf,
    TotalSecuritiesMatured,
    TotalSecuritiesMaturedChf,
    TotalSecuritiesMaturedFf,
    TotalSecuritiesPurchasedChf,
    TotalSecuritiesPurchasedFf,
    TotalSecuritiesSold,
    TotalSecuritiesSoldChf,
    TotalSecuritiesSoldFf,
    TotalSecurityCredits,
    TotalSecurityDebits,
    TotalTrustCredits,
    TotalTrustDebits,
    TotalUniversalCredits,
    TotalUniversalDebits,
    TotalValueDatedFunds,
    TotalWireTransfersInCHF,
    TotalWireTransfersInFF,
    TotalWireTransfersOutChf,
    TotalWireTransfersOutFf,
    TotalYtdAdjustment,
    TotalZbaCredits,
    TotalZbaDebits,
    TransferCalculation,
    TransferCalculationDebit,
    TwoOrMoreDaysFloat,
    Unknown,
    ZeroDayFloat,
}

impl AmountType {
    fn parse(type_code: &str) -> AmountType {
        let code = parse_string(type_code);

        match type_code {
            "010" => AmountType::Status(code, AmountSubtype::OpeningLedger),
            "011" => AmountType::Status(code, AmountSubtype::AverageOpeningLedgerMtd),
            "012" => AmountType::Status(code, AmountSubtype::AverageOpeningLedgerYtd),
            "015" => AmountType::Status(code, AmountSubtype::ClosingLedger),
            "020" => AmountType::Status(code, AmountSubtype::AverageClosingLedgerMtd),
            "021" => AmountType::Status(code, AmountSubtype::AverageClosingLedgerPreviousMonth),
            "022" => AmountType::Status(code, AmountSubtype::AggregateBalanceAdjustments),
            "024" => AmountType::Status(code, AmountSubtype::AverageClosingLedgerYtdPreviousMonth),
            "025" => AmountType::Status(code, AmountSubtype::AverageClosingLedgerYtd),
            "030" => AmountType::Status(code, AmountSubtype::CurrentLedger),
            "037" => AmountType::Status(code, AmountSubtype::AchNetPosition),
            "039" => AmountType::Status(
                code,
                AmountSubtype::OpeningAvailableAndTotalSameDayAchDtcDeposit,
            ),
            "040" => AmountType::Status(code, AmountSubtype::OpeningAvailable),
            "041" => AmountType::Status(code, AmountSubtype::AverageOpeningAvailableMtd),
            "042" => AmountType::Status(code, AmountSubtype::AverageOpeningAvailableYtd),
            "043" => AmountType::Status(code, AmountSubtype::AverageAvailablePreviousMonth),
            "044" => AmountType::Status(code, AmountSubtype::DisbursingOpeningAvailableBalance),
            "045" => AmountType::Status(code, AmountSubtype::ClosingAvailable),
            "050" => AmountType::Status(code, AmountSubtype::AverageClosingAvailableMtd),
            "051" => AmountType::Status(code, AmountSubtype::AverageClosingAvailableLastMonth),
            "054" => AmountType::Status(code, AmountSubtype::AverageClosingAvailableYtdLastMonth),
            "055" => AmountType::Status(code, AmountSubtype::AverageClosingAvailableYtd),
            "056" => AmountType::Status(code, AmountSubtype::LoanBalance),
            "057" => AmountType::Status(code, AmountSubtype::TotalInvestmentPosition),
            "059" => AmountType::Status(code, AmountSubtype::CurrentAvailableCrsSupressed),
            "060" => AmountType::Status(code, AmountSubtype::CurrentAvailable),
            "061" => AmountType::Status(code, AmountSubtype::AverageCurrentAvailableMtd),
            "062" => AmountType::Status(code, AmountSubtype::AverageCurrentAvailableYtd),
            "063" => AmountType::Status(code, AmountSubtype::TotalFloat),
            "065" => AmountType::Status(code, AmountSubtype::TargetBalance),
            "066" => AmountType::Status(code, AmountSubtype::AdjustedBalance),
            "067" => AmountType::Status(code, AmountSubtype::AdjustedBalanceMtd),
            "068" => AmountType::Status(code, AmountSubtype::AdjustedBalanceYtd),
            "070" => AmountType::Status(code, AmountSubtype::ZeroDayFloat),
            "072" => AmountType::Status(code, AmountSubtype::OneDayFloat),
            "073" => AmountType::Status(code, AmountSubtype::FloatAdjustment),
            "074" => AmountType::Status(code, AmountSubtype::TwoOrMoreDaysFloat),
            "075" => AmountType::Status(code, AmountSubtype::ThreeOrMoreDaysFloat),
            "076" => AmountType::Status(code, AmountSubtype::AdjustmentToBalances),
            "077" => AmountType::Status(code, AmountSubtype::AverageAdjustmentToBalancesMtd),
            "078" => AmountType::Status(code, AmountSubtype::AverageAdjustmentToBalancesYtd),
            "079" => AmountType::Status(code, AmountSubtype::FourDayFloat),
            "080" => AmountType::Status(code, AmountSubtype::FiveDayFloat),
            "081" => AmountType::Status(code, AmountSubtype::SixDayFloat),
            "082" => AmountType::Status(code, AmountSubtype::Average1DayFloatMtd),
            "083" => AmountType::Status(code, AmountSubtype::Average1DayFloatYtd),
            "084" => AmountType::Status(code, AmountSubtype::Average2DayFloatMtd),
            "085" => AmountType::Status(code, AmountSubtype::Average2DayFloatYtd),
            "086" => AmountType::Status(code, AmountSubtype::TransferCalculation),
            "100" => AmountType::CreditSummary(code, AmountSubtype::TotalCredits),
            "101" => AmountType::CreditSummary(code, AmountSubtype::TotalCreditAmountMtd),
            "105" => AmountType::CreditSummary(code, AmountSubtype::CreditsNotDetailed),
            "106" => AmountType::CreditSummary(code, AmountSubtype::DepositsSubjectToFloat),
            "107" => AmountType::CreditSummary(code, AmountSubtype::TotalAdjustmentCreditsYtd),
            "109" => AmountType::CreditSummary(code, AmountSubtype::CurrentDayTotalLockboxDeposits),
            "110" => AmountType::CreditSummary(code, AmountSubtype::TotalLockboxDeposits),
            "120" => AmountType::CreditSummary(code, AmountSubtype::EdiTransactionCredit),
            "130" => AmountType::CreditSummary(code, AmountSubtype::TotalConcentrationCredits),
            "131" => AmountType::CreditSummary(code, AmountSubtype::TotalDtcCredits),
            "140" => AmountType::CreditSummary(code, AmountSubtype::TotalAchCredits),
            "146" => AmountType::CreditSummary(code, AmountSubtype::TotalBankCardDeposits),
            "150" => {
                AmountType::CreditSummary(code, AmountSubtype::TotalPreauthorizedPaymentCredits)
            }
            "160" => {
                AmountType::CreditSummary(code, AmountSubtype::TotalAchDisbursingFundingCredits)
            }
            "162" => {
                AmountType::CreditSummary(code, AmountSubtype::CorporateTradePaymentSettlement)
            }
            "163" => AmountType::CreditSummary(code, AmountSubtype::CorporateTradePaymentCredits),
            "167" => AmountType::CreditSummary(code, AmountSubtype::AchSettlementCredits),
            "170" => AmountType::CreditSummary(code, AmountSubtype::TotalOtherCheckDeposits),
            "178" => AmountType::CreditSummary(code, AmountSubtype::ListPostCredits),
            "180" => AmountType::CreditSummary(code, AmountSubtype::TotalLoanProceeds),
            "182" => AmountType::CreditSummary(code, AmountSubtype::TotalBankPreparedDeposits),
            "185" => AmountType::CreditSummary(code, AmountSubtype::TotalMiscellaneousDeposits),
            "186" => AmountType::CreditSummary(code, AmountSubtype::TotalCashLetterCredits),
            "188" => AmountType::CreditSummary(code, AmountSubtype::TotalCashLetterAdjustments),
            "190" => AmountType::CreditSummary(code, AmountSubtype::TotalIncomingMoneyTransfers),
            "200" => AmountType::CreditSummary(code, AmountSubtype::TotalAutomaticTransferCredits),
            "205" => AmountType::CreditSummary(code, AmountSubtype::TotalBookTransferCredits),
            "207" => AmountType::CreditSummary(
                code,
                AmountSubtype::TotalInternationalMoneyTransferCredits,
            ),
            "210" => AmountType::CreditSummary(code, AmountSubtype::TotalInternationalCredits),
            "215" => AmountType::CreditSummary(code, AmountSubtype::TotalLettersOfCredit),
            "230" => AmountType::CreditSummary(code, AmountSubtype::TotalSecurityCredits),
            "231" => AmountType::CreditSummary(code, AmountSubtype::TotalCollectionCredits),
            "239" => AmountType::CreditSummary(code, AmountSubtype::TotalBankersAcceptanceCredits),
            "245" => AmountType::CreditSummary(code, AmountSubtype::MonthlyDividends),
            "250" => AmountType::CreditSummary(code, AmountSubtype::TotalChecksPostedAndReturned),
            "251" => AmountType::CreditSummary(code, AmountSubtype::TotalDebitReversals),
            "256" => AmountType::CreditSummary(code, AmountSubtype::TotalAchReturnItems),
            "260" => AmountType::CreditSummary(code, AmountSubtype::TotalRejectedCredits),
            "270" => AmountType::CreditSummary(code, AmountSubtype::TotalZbaCredits),
            "271" => AmountType::CreditSummary(code, AmountSubtype::NetZeroBalanceAmount),
            "280" => {
                AmountType::CreditSummary(code, AmountSubtype::TotalControlledDisbursingCredits)
            }
            "285" => AmountType::CreditSummary(code, AmountSubtype::TotalDtcDisbursingCredits),
            "294" => AmountType::CreditSummary(code, AmountSubtype::TotalAtmCredits),
            "302" => AmountType::CreditSummary(code, AmountSubtype::CorrespondentBankDeposit),
            "303" => AmountType::CreditSummary(code, AmountSubtype::TotalWireTransfersInFF),
            "304" => AmountType::CreditSummary(code, AmountSubtype::TotalWireTransfersInCHF),
            "305" => AmountType::CreditSummary(code, AmountSubtype::TotalFedFundsSold),
            "307" => AmountType::CreditSummary(code, AmountSubtype::TotalTrustCredits),
            "309" => AmountType::CreditSummary(code, AmountSubtype::TotalValueDatedFunds),
            "310" => AmountType::CreditSummary(code, AmountSubtype::TotalCommercialDeposits),
            "315" => AmountType::CreditSummary(code, AmountSubtype::TotalInternationalCreditsFf),
            "316" => AmountType::CreditSummary(code, AmountSubtype::TotalInternationalCreditsChf),
            "318" => AmountType::CreditSummary(code, AmountSubtype::TotalForeignCheckPurchased),
            "319" => AmountType::CreditSummary(code, AmountSubtype::LateDeposit),
            "320" => AmountType::CreditSummary(code, AmountSubtype::TotalSecuritiesSoldFf),
            "321" => AmountType::CreditSummary(code, AmountSubtype::TotalSecuritiesSoldChf),
            "324" => AmountType::CreditSummary(code, AmountSubtype::TotalSecuritiesMaturedFf),
            "325" => AmountType::CreditSummary(code, AmountSubtype::TotalSecuritiesMaturedChf),
            "326" => AmountType::CreditSummary(code, AmountSubtype::TotalSecuritiesInterest),
            "327" => AmountType::CreditSummary(code, AmountSubtype::TotalSecuritiesMatured),
            "328" => AmountType::CreditSummary(code, AmountSubtype::TotalSecuritiesInterestFf),
            "329" => AmountType::CreditSummary(code, AmountSubtype::TotalSecuritiesInterestChf),
            "330" => AmountType::CreditSummary(code, AmountSubtype::TotalEscrowCredits),
            "332" => AmountType::CreditSummary(
                code,
                AmountSubtype::TotalMiscellaneousSecuritiesCreditsFf,
            ),
            "336" => AmountType::CreditSummary(
                code,
                AmountSubtype::TotalMiscellaneousSecuritiesCreditsChf,
            ),
            "338" => AmountType::CreditSummary(code, AmountSubtype::TotalSecuritiesSold),
            "340" => AmountType::CreditSummary(code, AmountSubtype::TotalBrokerDeposits),
            "341" => AmountType::CreditSummary(code, AmountSubtype::TotalBrokerDepositsFf),
            "343" => AmountType::CreditSummary(code, AmountSubtype::TotalBrokerDepositsChf),
            "350" => AmountType::CreditSummary(code, AmountSubtype::InvestmentSold),
            "352" => AmountType::CreditSummary(code, AmountSubtype::TotalCashCenterCredits),
            "355" => AmountType::CreditSummary(code, AmountSubtype::InvestmentInterest),
            "356" => AmountType::CreditSummary(code, AmountSubtype::TotalCreditAdjustment),
            "360" => AmountType::CreditSummary(
                code,
                AmountSubtype::TotalCreditsLessWireTransferAndReturnedChecks,
            ),
            "361" => AmountType::CreditSummary(
                code,
                AmountSubtype::GrandTotalCreditsLessGrandTotalDebits,
            ),
            "370" => AmountType::CreditSummary(code, AmountSubtype::TotalBackValueCredits),
            "385" => AmountType::CreditSummary(code, AmountSubtype::TotalUniversalCredits),
            "389" => AmountType::CreditSummary(code, AmountSubtype::TotalFreightPaymentCredits),
            "390" => AmountType::CreditSummary(code, AmountSubtype::TotalMiscellaneousCredits),
            "400" => AmountType::DebitSummary(code, AmountSubtype::TotalDebits),
            "401" => AmountType::DebitSummary(code, AmountSubtype::TotalDebitAmountMtd),
            "403" => AmountType::DebitSummary(code, AmountSubtype::TodaysTotalDebits),
            "405" => AmountType::DebitSummary(
                code,
                AmountSubtype::TotalDebitLessWireTransfersAndChargeBacks,
            ),
            "406" => AmountType::DebitSummary(code, AmountSubtype::DebitsNotDetailed),
            "410" => AmountType::DebitSummary(code, AmountSubtype::TotalYtdAdjustment),
            "412" => {
                AmountType::DebitSummary(code, AmountSubtype::TotalDebitsExcludingReturnedItems)
            }
            "416" => AmountType::DebitSummary(code, AmountSubtype::TotalLockboxDebits),
            "420" => AmountType::DebitSummary(code, AmountSubtype::EdiTransactionDebits),
            "430" => AmountType::DebitSummary(code, AmountSubtype::TotalPayableThroughDrafts),
            "446" => {
                AmountType::DebitSummary(code, AmountSubtype::TotalAchDisbursementFundingDebits)
            }
            "450" => AmountType::DebitSummary(code, AmountSubtype::TotalAchDebits),
            "463" => AmountType::DebitSummary(code, AmountSubtype::CorporateTradePaymentDebits),
            "465" => AmountType::DebitSummary(code, AmountSubtype::CorporateTradePaymentSettlement),
            "467" => AmountType::DebitSummary(code, AmountSubtype::AchSettlementDebits),
            "470" => AmountType::DebitSummary(code, AmountSubtype::TotalCheckPaid),
            "471" => AmountType::DebitSummary(code, AmountSubtype::TotalCheckPaidCumulativeMtd),
            "478" => AmountType::DebitSummary(code, AmountSubtype::ListPostDebits),
            "480" => AmountType::DebitSummary(code, AmountSubtype::TotalLoanPayments),
            "482" => AmountType::DebitSummary(code, AmountSubtype::TotalBankOriginatedDebits),
            "486" => AmountType::DebitSummary(code, AmountSubtype::TotalCashLetterDebits),
            "490" => AmountType::DebitSummary(code, AmountSubtype::TotalOutgoingMoneyTransfers),
            "500" => AmountType::DebitSummary(code, AmountSubtype::TotalAutomaticTransferDebits),
            "505" => AmountType::DebitSummary(code, AmountSubtype::TotalBookTransferDebits),
            "507" => {
                AmountType::DebitSummary(code, AmountSubtype::TotalInternationalMoneyTransferDebits)
            }
            "510" => AmountType::DebitSummary(code, AmountSubtype::TotalInternationalDebits),
            "515" => AmountType::DebitSummary(code, AmountSubtype::TotalLettersOfCredit),
            "530" => AmountType::DebitSummary(code, AmountSubtype::TotalSecurityDebits),
            "532" => {
                AmountType::DebitSummary(code, AmountSubtype::TotalAmountOfSecuritiesPurchased)
            }
            "534" => {
                AmountType::DebitSummary(code, AmountSubtype::TotalMiscellaneousSecuritiesDbFf)
            }
            "536" => {
                AmountType::DebitSummary(code, AmountSubtype::TotalMiscellaneousSecuritiesDebitChf)
            }
            "537" => AmountType::DebitSummary(code, AmountSubtype::TotalCollectionDebit),
            "539" => AmountType::DebitSummary(code, AmountSubtype::TotalBankersAcceptancesDebit),
            "550" => AmountType::DebitSummary(code, AmountSubtype::TotalDepositedItemsReturned),
            "551" => AmountType::DebitSummary(code, AmountSubtype::TotalCreditReversals),
            "556" => AmountType::DebitSummary(code, AmountSubtype::TotalAchReturnItems),
            "560" => AmountType::DebitSummary(code, AmountSubtype::TotalRejectedDebits),
            "570" => AmountType::DebitSummary(code, AmountSubtype::TotalZbaDebits),
            "580" => AmountType::DebitSummary(code, AmountSubtype::TotalControlledDisbursingDebits),
            "583" => {
                AmountType::DebitSummary(code, AmountSubtype::TotalDisbursingChecksPaidEarlyAmount)
            }
            "584" => {
                AmountType::DebitSummary(code, AmountSubtype::TotalDisbursingChecksPaidLaterAmount)
            }
            "585" => AmountType::DebitSummary(code, AmountSubtype::DisbursingFundingRequirement),
            "586" => AmountType::DebitSummary(code, AmountSubtype::FrbPresentmentEstimate),
            "587" => AmountType::DebitSummary(code, AmountSubtype::LateDebitsAfterNotification),
            "588" => {
                AmountType::DebitSummary(code, AmountSubtype::TotalDisbursingChecksPaidLastAmount)
            }
            "590" => AmountType::DebitSummary(code, AmountSubtype::TotalDtcDebits),
            "594" => AmountType::DebitSummary(code, AmountSubtype::TotalAtmDebits),
            "596" => AmountType::DebitSummary(code, AmountSubtype::TotalAprDebits),
            "601" => AmountType::DebitSummary(code, AmountSubtype::EstimatedTotalDisbursement),
            "602" => AmountType::DebitSummary(code, AmountSubtype::AdjustedTotalDisbursement),
            "610" => AmountType::DebitSummary(code, AmountSubtype::TotalFundsRequired),
            "611" => AmountType::DebitSummary(code, AmountSubtype::TotalWireTransfersOutChf),
            "612" => AmountType::DebitSummary(code, AmountSubtype::TotalWireTransfersOutFf),
            "613" => AmountType::DebitSummary(code, AmountSubtype::TotalInternationalDebitChf),
            "614" => AmountType::DebitSummary(code, AmountSubtype::TotalInternationalDebitFf),
            "615" => AmountType::DebitSummary(
                code,
                AmountSubtype::TotalFederalReserveBankCommercialBankDebit,
            ),
            "617" => AmountType::DebitSummary(code, AmountSubtype::TotalSecuritiesPurchasedChf),
            "618" => AmountType::DebitSummary(code, AmountSubtype::TotalSecuritiesPurchasedFf),
            "621" => AmountType::DebitSummary(code, AmountSubtype::TotalBrokerDebitsChf),
            "623" => AmountType::DebitSummary(code, AmountSubtype::TotalBrokerDebitsFf),
            "625" => AmountType::DebitSummary(code, AmountSubtype::TotalBrokerDebits),
            "626" => AmountType::DebitSummary(code, AmountSubtype::TotalFedFundsPurchased),
            "628" => AmountType::DebitSummary(code, AmountSubtype::TotalCashCenterDebits),
            "630" => AmountType::DebitSummary(code, AmountSubtype::TotalDebitAdjustments),
            "632" => AmountType::DebitSummary(code, AmountSubtype::TotalTrustDebits),
            "640" => AmountType::DebitSummary(code, AmountSubtype::TotalEscrowDebits),
            "646" => AmountType::DebitSummary(code, AmountSubtype::TransferCalculationDebit),
            "650" => AmountType::DebitSummary(code, AmountSubtype::InvestmentsPurchased),
            "655" => AmountType::DebitSummary(code, AmountSubtype::TotalInvestmentInterestDebits),
            "665" => AmountType::DebitSummary(code, AmountSubtype::InterceptDebits),
            "670" => AmountType::DebitSummary(code, AmountSubtype::TotalBackValueDebits),
            "685" => AmountType::DebitSummary(code, AmountSubtype::TotalUniversalDebits),
            "689" => AmountType::DebitSummary(code, AmountSubtype::FrbFreightPaymentDebits),
            "690" => AmountType::DebitSummary(code, AmountSubtype::TotalMiscellaneousDebits),
            "701" => AmountType::Status(code, AmountSubtype::PrincipalLoanBalance),
            "703" => AmountType::Status(code, AmountSubtype::AvailableCommitmentAmount),
            "705" => AmountType::Status(code, AmountSubtype::PaymentAmountDue),
            "707" => AmountType::Status(code, AmountSubtype::PrincipalAmountPastDue),
            "709" => AmountType::Status(code, AmountSubtype::InterestAmountPastDue),
            "720" => AmountType::CreditSummary(code, AmountSubtype::TotalLoanPayment),
            "760" => AmountType::DebitSummary(code, AmountSubtype::LoanDisbursement),
            other_code => match other_code.parse::<i16>() {
                Ok(n) if n >= 900 && n <= 919 => {
                    AmountType::Status(code, AmountSubtype::CustomStatus)
                }
                Ok(n) if n >= 920 && n <= 959 => {
                    AmountType::CreditSummary(code, AmountSubtype::CustomCreditSummary)
                }
                Ok(n) if n >= 960 && n <= 999 => {
                    AmountType::DebitSummary(code, AmountSubtype::CustomDebitSummary)
                }
                _ => AmountType::Unknown(code, AmountSubtype::Unknown),
            },
        }
    }
}

impl Serialize for AmountType {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let (code, type_name, sub_type) = match *self {
            AmountType::Status(ref c, ref t) => (c, "status", t),
            AmountType::CreditSummary(ref c, ref t) => (c, "credit_summary", t),
            AmountType::DebitSummary(ref c, ref t) => (c, "debit_summary", t),
            AmountType::Unknown(ref c, ref t) => (c, "unknown", t),
        };

        let mut state = serializer.serialize_struct("AmountType", 3)?;
        state.serialize_field("code", code)?;
        state.serialize_field("type", type_name)?;
        state.serialize_field("subtype", sub_type)?;
        state.end()
    }
}