using Schaad.Finance.Api;
using Schaad.Finance.Api.AccountStatements;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Schaad.Finance.Formats.AccountStatements
{
///
/// Reads MT940 format
/// http://www.kontopruef.de/mt940s.shtml
///
public class MT940 : IAccountStatementParsingService
{
public List ReadFile(string filePath, Encoding encoding)
{
var accountList = new List();
AccountStatement account = null;
Transaction currentTransaction = null;
List transactionTexts = null;
foreach (var line in File.ReadLines(filePath, encoding))
{
if (line.Length < 3)
{
continue;
}
var codeValue = ParseLine(line);
switch (codeValue.Item1)
{
// Account
case ":25:":
account = new AccountStatement();
accountList.Add(account);
account.AccountNumber = codeValue.Item2;
break;
// Start balance
case ":60F:":
account.StartBalance = ParseBalance(codeValue.Item2);
break;
// End balance
case ":62F:":
account.EndBalance = ParseBalance(codeValue.Item2);
break;
// Transaction
case ":61:":
// have we an old transaction
if (currentTransaction != null && transactionTexts.Any())
{
currentTransaction.Text = string.Join(", ", transactionTexts);
}
transactionTexts = new List();
currentTransaction = ParseTransaction(codeValue.Item2);
account.Transactions.Add(currentTransaction);
break;
// Transaction text
case ":NS:":
//if (currentTransaction != null) currentTransaction.Text = codeValue.Item2;
break;
}
if (codeValue.Item1.IndexOf(':') == -1 && currentTransaction != null)
{
if (transactionTexts.Contains(codeValue.Item2) == false)
{
transactionTexts.Add(codeValue.Item2);
}
}
}
// have we an old transaction
if (currentTransaction != null && transactionTexts.Any())
{
currentTransaction.Text = string.Join(", ", transactionTexts);
}
return accountList;
}
public string ValidateAccountStatement(AccountStatement accountStatement)
{
if (accountStatement.StartBalance.Currency != accountStatement.EndBalance.Currency)
{
return $"Startbalance currency ({accountStatement.StartBalance.Currency}) does not match endbalance currency ({accountStatement.EndBalance.Currency})";
}
var transactionSum = Math.Round(accountStatement.Transactions.Sum(t => t.Value), 2);
var end = accountStatement.StartBalance.Value + transactionSum;
if (end != accountStatement.EndBalance.Value)
{
return $"Enbalance ({accountStatement.EndBalance.Value}) does not match startbalance ({accountStatement.StartBalance.Value}) + transactions ({transactionSum})";
}
return string.Empty;
}
///
/// Parse a line
///
///
///
private Tuple ParseLine(string line)
{
if (line.StartsWith(":"))
{
var pos = line.IndexOf(':', 1);
var code = line.Substring(0, pos + 1);
return new Tuple(code, line.Substring(code.Length));
}
else
return new Tuple(line.Substring(0, 2), line.Substring(2));
}
///
/// Parse a balance
///
///
///
private Balance ParseBalance(string value)
{
var balance = new Balance();
bool isDebit = false;
if (value.First() == 'C')
isDebit = false;
else if (value.First() == 'D')
isDebit = true;
value = value.Remove(0, 1);
balance.BookingDate = ParseDate(value.Substring(0, 6));
value = value.Remove(0, 6);
balance.Currency = value.Substring(0, 3);
value = value.Remove(0, 3);
balance.Value = ParseBalanceValue(value);
if (isDebit)
balance.Value *= -1.0;
return balance;
}
private double ParseBalanceValue(string balanceValue)
{
return Double.Parse(balanceValue.Replace(',', '.'));
}
///
/// Parse a transaction
///
///
///
private Transaction ParseTransaction(string value)
{
var transaction = new Transaction();
transaction.ValueDate = ParseDate(value.Substring(0, 6));
value = value.Remove(0, 6);
string year = (transaction.ValueDate.Year - 2000).ToString();
transaction.BookingDate = ParseDate(year + value.Substring(0, 4));
value = value.Remove(0, 4);
bool isDebit = false;
if (value.First() == 'C')
isDebit = false;
else if (value.First() == 'D')
isDebit = true;
else
throw new NotSupportedException("Code not supported: " + value.First());
value = value.Remove(0, 1);
transaction.Value = ParseBalanceValue(value.Substring(0, 15));
value = value.Remove(0, 15);
if (isDebit)
transaction.Value *= -1.0;
return transaction;
}
///
/// Parse a date in format: JJMMTT
///
///
///
private DateTime ParseDate(string dateValue)
{
if (dateValue.Length != 6)
throw new Exception("Wrong format for date. Need: JJMMTT");
return new DateTime(2000 + Int32.Parse(dateValue.Substring(0, 2)), Int32.Parse(dateValue.Substring(2, 2)), Int32.Parse(dateValue.Substring(4, 2)));
}
}
}