2019-12-22 21:18:45 +00:00
|
|
|
use chrono::naive::NaiveDate;
|
2019-12-22 21:30:26 +00:00
|
|
|
use std::collections::HashSet;
|
2019-12-22 21:18:45 +00:00
|
|
|
use string_cache::DefaultAtom;
|
2019-12-22 21:30:26 +00:00
|
|
|
|
|
|
|
/// simple typed helper enums
|
|
|
|
mod simple_enums;
|
|
|
|
use simple_enums::*;
|
2019-12-22 21:18:45 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
|
|
|
enum KontoDaten {
|
|
|
|
None,
|
2019-12-22 21:30:26 +00:00
|
|
|
Old { knr: u64, blz: u32 },
|
|
|
|
New { iban: String, bic: String },
|
2019-12-22 21:18:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type TransactionValue = fixed::types::U21F11;
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
struct TransactionLine {
|
|
|
|
d_buchungs: NaiveDate,
|
|
|
|
d_valuta: NaiveDate,
|
|
|
|
// ignore 3. column = "Auftraggeber/Zahlungsempfänger",
|
|
|
|
// because that should be nearly equivalent for every row
|
|
|
|
p_other: DefaultAtom,
|
|
|
|
konto_data: KontoDaten,
|
|
|
|
verwendungszw: String,
|
|
|
|
kref: String,
|
|
|
|
waehrung: Waehrung,
|
|
|
|
umsatz: TransactionValue,
|
|
|
|
direction: TransactionDirection,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
|
|
struct Transactions {
|
|
|
|
pothc: HashSet<DefaultAtom>,
|
|
|
|
elems: Vec<TransactionLine>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::ops::Deref for Transactions {
|
|
|
|
type Target = [TransactionLine];
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn deref(&self) -> &[TransactionLine] {
|
|
|
|
&self.elems[..]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Transactions {
|
|
|
|
#[inline]
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Default::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.elems.is_empty()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.elems.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn push(&mut self, mut tl: TransactionLine) {
|
|
|
|
if let Some(x) = self.pothc.get(&tl.p_other) {
|
|
|
|
tl.p_other = x.clone();
|
|
|
|
} else {
|
|
|
|
self.pothc.insert(tl.p_other.clone());
|
|
|
|
}
|
|
|
|
self.elems.push(tl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 16:55:38 +00:00
|
|
|
fn main() {
|
|
|
|
use encoding::types::Encoding;
|
|
|
|
|
|
|
|
for i in std::env::args().skip(1) {
|
|
|
|
println!("F = {}", i);
|
|
|
|
|
|
|
|
let fh = readfilez::read_from_file(std::fs::File::open(i)).expect("unable to open file");
|
|
|
|
|
|
|
|
let dat = encoding::all::ISO_8859_1
|
|
|
|
.decode(&*fh, encoding::types::DecoderTrap::Replace)
|
|
|
|
.expect("got invalid latin-1 data");
|
|
|
|
|
2019-12-22 21:18:45 +00:00
|
|
|
std::mem::drop(fh);
|
|
|
|
|
2019-12-22 16:55:38 +00:00
|
|
|
let mut rdr = csv::ReaderBuilder::new()
|
|
|
|
.delimiter(b';')
|
|
|
|
.flexible(true)
|
|
|
|
.has_headers(false)
|
|
|
|
.from_reader(dat.as_bytes());
|
|
|
|
|
2019-12-22 21:18:45 +00:00
|
|
|
let mut recsit = rdr.records().skip(8);
|
|
|
|
|
2019-12-22 21:30:26 +00:00
|
|
|
assert_eq!(
|
|
|
|
recsit.next().unwrap().unwrap(),
|
|
|
|
vec![
|
|
|
|
"Buchungstag",
|
|
|
|
"Valuta",
|
|
|
|
"Auftraggeber/Zahlungsempfänger",
|
|
|
|
"Empfänger/Zahlungspflichtiger",
|
|
|
|
"Konto-Nr.",
|
|
|
|
"IBAN",
|
|
|
|
"BLZ",
|
|
|
|
"BIC",
|
|
|
|
"Vorgang/Verwendungszweck",
|
|
|
|
"Kundenreferenz",
|
|
|
|
"Währung",
|
|
|
|
"Umsatz",
|
|
|
|
" "
|
|
|
|
]
|
|
|
|
);
|
2019-12-22 21:18:45 +00:00
|
|
|
|
|
|
|
println!("\nData:");
|
|
|
|
for result in recsit {
|
2019-12-22 16:55:38 +00:00
|
|
|
let record = result.expect("got invalid line");
|
2019-12-22 21:18:45 +00:00
|
|
|
assert_eq!(record.len(), 13);
|
|
|
|
if record[1].is_empty() {
|
|
|
|
// finalizer line -> ignore
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode KontoDaten
|
2019-12-22 21:30:26 +00:00
|
|
|
let r_e: &[u8] = &[
|
|
|
|
record[4].is_empty() as u8,
|
|
|
|
record[5].is_empty() as u8,
|
|
|
|
record[6].is_empty() as u8,
|
|
|
|
record[7].is_empty() as u8,
|
|
|
|
];
|
2019-12-22 21:18:45 +00:00
|
|
|
let konto_data = match &*r_e {
|
|
|
|
&[0, 1, 0, 1] => KontoDaten::Old {
|
|
|
|
knr: record[4].parse().expect("invalid Konto-Nr."),
|
|
|
|
blz: record[6].parse().expect("invalid BLZ"),
|
|
|
|
},
|
|
|
|
&[1, 0, 1, 0] => KontoDaten::New {
|
|
|
|
iban: record[5].to_string(),
|
|
|
|
bic: record[7].to_string(),
|
|
|
|
},
|
|
|
|
&[1, 1, 1, 1] => KontoDaten::None,
|
2019-12-22 21:30:26 +00:00
|
|
|
_ => unimplemented!(
|
|
|
|
"unsupported KontoData encoding: {:?} :in: {:?}",
|
|
|
|
&r_e,
|
|
|
|
&record
|
|
|
|
),
|
2019-12-22 21:18:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let tl = TransactionLine {
|
2019-12-22 21:30:26 +00:00
|
|
|
d_buchungs: NaiveDate::parse_from_str(&record[0], "%d.%m.%Y")
|
|
|
|
.expect("invalid date format"),
|
|
|
|
d_valuta: NaiveDate::parse_from_str(&record[1], "%d.%m.%Y")
|
|
|
|
.expect("invalid date format"),
|
2019-12-22 21:18:45 +00:00
|
|
|
p_other: DefaultAtom::from(&record[3]),
|
|
|
|
konto_data,
|
|
|
|
verwendungszw: record[8].to_string(),
|
|
|
|
kref: record[9].to_string(),
|
|
|
|
waehrung: record[10].parse().unwrap(),
|
2019-12-22 21:30:26 +00:00
|
|
|
umsatz: record[11]
|
|
|
|
.replace('.', "")
|
|
|
|
.replace(',', ".")
|
|
|
|
.parse()
|
|
|
|
.expect("invalid Umsatz"),
|
2019-12-22 21:18:45 +00:00
|
|
|
direction: record[12].parse().unwrap(),
|
|
|
|
};
|
|
|
|
println!("{:?}", tl);
|
2019-12-22 16:55:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|