Initital
This commit is contained in:
commit
6da5187e4b
31 changed files with 15457 additions and 0 deletions
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
################################################################################
|
||||
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
|
||||
################################################################################
|
||||
|
||||
/Schaad.Finance/bin/Debug
|
||||
/Schaad.Finance/obj
|
||||
*.user
|
||||
/Schaad.Finance.Api/bin/Debug/netstandard2.0
|
||||
/Schaad.Finance.Api/obj
|
||||
/Schaad.Finance.Tests/bin/Debug/netcoreapp2.0
|
||||
/Schaad.Finance.Tests/obj
|
||||
/Schaad.Finance.Api/.vs/Schaad.Finances.Api
|
||||
/.vs/Schaad.Finance
|
||||
42
Schaad.Finance.Api/AccountStatements/AccountStatement.cs
Normal file
42
Schaad.Finance.Api/AccountStatements/AccountStatement.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Schaad.Finance.Api.AccountStatements
|
||||
{
|
||||
public class AccountStatement
|
||||
{
|
||||
/// <summary>
|
||||
/// The account number (IBAN)
|
||||
/// </summary>
|
||||
public string AccountNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Account Currency
|
||||
/// </summary>
|
||||
public string Currency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The starting balance
|
||||
/// </summary>
|
||||
public Balance StartBalance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The end balance
|
||||
/// </summary>
|
||||
public Balance EndBalance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Summary of all credit transactions (camt)
|
||||
/// </summary>
|
||||
public TransactionSummary CreditTransactionSummary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Summary of all debit transactions (camt)
|
||||
/// </summary>
|
||||
public TransactionSummary DebitTransactionSummary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of transactions
|
||||
/// </summary>
|
||||
public List<Transaction> Transactions { get; set; } = new List<Transaction>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
namespace Schaad.Finance.Api.AccountStatements
|
||||
{
|
||||
public class AccountStatementResult
|
||||
{
|
||||
public readonly AccountStatement AccountStatement;
|
||||
|
||||
public readonly bool IsSuccess;
|
||||
|
||||
public readonly string Error;
|
||||
|
||||
public AccountStatementResult(AccountStatement accountStatement, string error)
|
||||
{
|
||||
AccountStatement = accountStatement;
|
||||
Error = error;
|
||||
IsSuccess = string.IsNullOrEmpty(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Schaad.Finance.Api/AccountStatements/Balance.cs
Normal file
25
Schaad.Finance.Api/AccountStatements/Balance.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Schaad.Finance.Api.AccountStatements
|
||||
{
|
||||
/// <summary>
|
||||
/// Detailed balance info
|
||||
/// </summary>
|
||||
public class Balance
|
||||
{
|
||||
/// <summary>
|
||||
/// Date of balance
|
||||
/// </summary>
|
||||
public DateTime BookingDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Currency of balance
|
||||
/// </summary>
|
||||
public string Currency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value
|
||||
/// </summary>
|
||||
public double Value { get; set; }
|
||||
}
|
||||
}
|
||||
57
Schaad.Finance.Api/AccountStatements/Transaction.cs
Normal file
57
Schaad.Finance.Api/AccountStatements/Transaction.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Schaad.Finance.Api.AccountStatements
|
||||
{
|
||||
/// <summary>
|
||||
/// A transaction
|
||||
/// </summary>
|
||||
public class Transaction
|
||||
{
|
||||
public string Id
|
||||
{
|
||||
set
|
||||
{
|
||||
id = value;
|
||||
}
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return GetHashString(ValueDate.ToString("yyyyMMdd") + Value + Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
private string id;
|
||||
|
||||
public DateTime ValueDate { get; set; }
|
||||
|
||||
public DateTime BookingDate { get; set; }
|
||||
|
||||
public double Value { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
|
||||
private string GetHashString(string inputString)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (byte b in GetHash(inputString))
|
||||
{
|
||||
sb.Append(b.ToString("X2"));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private byte[] GetHash(string inputString)
|
||||
{
|
||||
var algorithm = MD5.Create(); //or use SHA1.Create();
|
||||
return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace Schaad.Finance.Api.AccountStatements
|
||||
{
|
||||
public class TransactionSummary
|
||||
{
|
||||
public int NumberOfTransaction { get; set; }
|
||||
|
||||
public decimal Sum { get; set; }
|
||||
}
|
||||
}
|
||||
7
Schaad.Finance.Api/CreditCardProvider.cs
Normal file
7
Schaad.Finance.Api/CreditCardProvider.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace Schaad.Finance.Api
|
||||
{
|
||||
public enum CreditCardProvider
|
||||
{
|
||||
Cembra
|
||||
}
|
||||
}
|
||||
13
Schaad.Finance.Api/Datasets/CreditCardTransaction.cs
Normal file
13
Schaad.Finance.Api/Datasets/CreditCardTransaction.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Schaad.Finance.Api.Datasets
|
||||
{
|
||||
public class CreditCardTransaction
|
||||
{
|
||||
public DateTime BookingDate { get; set; }
|
||||
public DateTime TransactionDate { get; set; }
|
||||
public string Transaction { get; set; }
|
||||
public string TransactionType { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
}
|
||||
}
|
||||
12
Schaad.Finance.Api/IAccountStatementParsingService.cs
Normal file
12
Schaad.Finance.Api/IAccountStatementParsingService.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using Schaad.Finance.Api.AccountStatements;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Schaad.Finance.Api
|
||||
{
|
||||
public interface IAccountStatementParsingService
|
||||
{
|
||||
List<AccountStatement> ReadFile(string filePath, Encoding encoding);
|
||||
string ValidateAccountStatement(AccountStatement accountStatement);
|
||||
}
|
||||
}
|
||||
11
Schaad.Finance.Api/IAccountStatementService.cs
Normal file
11
Schaad.Finance.Api/IAccountStatementService.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using Schaad.Finance.Api.AccountStatements;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Schaad.Finance.Api
|
||||
{
|
||||
public interface IAccountStatementService
|
||||
{
|
||||
List<AccountStatementResult> ReadFile(string filePath, Encoding encoding);
|
||||
}
|
||||
}
|
||||
11
Schaad.Finance.Api/ICreditCardStatementService.cs
Normal file
11
Schaad.Finance.Api/ICreditCardStatementService.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using Schaad.Finance.Api.Datasets;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Schaad.Finance.Api
|
||||
{
|
||||
public interface ICreditCardStatementService
|
||||
{
|
||||
IReadOnlyList<CreditCardTransaction> ReadFile(CreditCardProvider creditCardProvider, string filePath, Encoding encoding);
|
||||
}
|
||||
}
|
||||
10
Schaad.Finance.Api/IFxService.cs
Normal file
10
Schaad.Finance.Api/IFxService.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace Schaad.Finance.Api
|
||||
{
|
||||
public interface IFxService
|
||||
{
|
||||
decimal ConvertCurrency(decimal amount, string fromCurrency, string toCurrency, string fixerIoApiKey);
|
||||
decimal GetFxRate(string fromCurrency, string toCurrency, DateTime date, string fixerIoApiKey);
|
||||
}
|
||||
}
|
||||
11
Schaad.Finance.Api/Schaad.Finance.Api.csproj
Normal file
11
Schaad.Finance.Api/Schaad.Finance.Api.csproj
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="ve-3237.tmp" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
33
Schaad.Finance.Tests/AccountStatementServiceTest.cs
Normal file
33
Schaad.Finance.Tests/AccountStatementServiceTest.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using NUnit.Framework;
|
||||
using Schaad.Finance.Services;
|
||||
|
||||
namespace Schaad.Finance.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class AccountStatementServiceTest
|
||||
{
|
||||
private AccountStatementService service;
|
||||
|
||||
[SetUp]
|
||||
public void Startup()
|
||||
{
|
||||
service = new AccountStatementService();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReadFile_MT940()
|
||||
{
|
||||
var statements = service.ReadFile(@"Data\mt940.sta", System.Text.Encoding.UTF8);
|
||||
|
||||
Assert.That(statements.TrueForAll(s => s.IsSuccess));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReadFile_CAMT053()
|
||||
{
|
||||
var statements = service.ReadFile(@"Data\camt053.xml", System.Text.Encoding.UTF8);
|
||||
|
||||
Assert.That(statements.TrueForAll(s => s.IsSuccess));
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Schaad.Finance.Tests/CreditCardStatementServiceTest.cs
Normal file
28
Schaad.Finance.Tests/CreditCardStatementServiceTest.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using NUnit.Framework;
|
||||
using Schaad.Finance.Api;
|
||||
using Schaad.Finance.Services;
|
||||
using System.Linq;
|
||||
|
||||
namespace Schaad.Finance.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class CreditCardStatementServiceTest
|
||||
{
|
||||
private CreditCardStatementService service;
|
||||
|
||||
[SetUp]
|
||||
public void Startup()
|
||||
{
|
||||
service = new CreditCardStatementService();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReadFile_Cembra()
|
||||
{
|
||||
var transactions = service.ReadFile(CreditCardProvider.Cembra, @"Data\cembra.csv", System.Text.Encoding.UTF8);
|
||||
|
||||
Assert.That(transactions.Count, Is.EqualTo(4));
|
||||
Assert.That(transactions.Sum(t => t.Amount), Is.EqualTo(144.60m));
|
||||
}
|
||||
}
|
||||
}
|
||||
162
Schaad.Finance.Tests/Data/camt053.xml
Normal file
162
Schaad.Finance.Tests/Data/camt053.xml
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.04">
|
||||
<BkToCstmrStmt>
|
||||
<GrpHdr>
|
||||
<MsgId>MSG-C053-190706014611-01</MsgId>
|
||||
<CreDtTm>2019-07-06T00:00:00</CreDtTm>
|
||||
<MsgPgntn>
|
||||
<PgNb>1</PgNb>
|
||||
<LastPgInd>true</LastPgInd>
|
||||
</MsgPgntn>
|
||||
<AddtlInf>RECONSTRUCTION</AddtlInf>
|
||||
</GrpHdr>
|
||||
<Stmt>
|
||||
<Id>STM-C053-190706014611-02</Id>
|
||||
<ElctrncSeqNb>999999</ElctrncSeqNb>
|
||||
<CreDtTm>2019-07-06T00:00:00</CreDtTm>
|
||||
<FrToDt>
|
||||
<FrDtTm>2019-07-01T00:00:00</FrDtTm>
|
||||
<ToDtTm>2019-07-05T00:00:00</ToDtTm>
|
||||
</FrToDt>
|
||||
<CpyDplctInd>DUPL</CpyDplctInd>
|
||||
<Acct>
|
||||
<Id>
|
||||
<IBAN>CH3789144975191379986</IBAN>
|
||||
</Id>
|
||||
<Ownr>
|
||||
<Nm>Schaad Claudio</Nm>
|
||||
</Ownr>
|
||||
</Acct>
|
||||
<Bal>
|
||||
<Tp>
|
||||
<CdOrPrtry>
|
||||
<Cd>OPBD</Cd>
|
||||
</CdOrPrtry>
|
||||
</Tp>
|
||||
<Amt Ccy="CHF">2468.71</Amt>
|
||||
<CdtDbtInd>CRDT</CdtDbtInd>
|
||||
<Dt>
|
||||
<Dt>2019-07-01</Dt>
|
||||
</Dt>
|
||||
</Bal>
|
||||
<Bal>
|
||||
<Tp>
|
||||
<CdOrPrtry>
|
||||
<Cd>CLBD</Cd>
|
||||
</CdOrPrtry>
|
||||
</Tp>
|
||||
<Amt Ccy="CHF">2444.01</Amt>
|
||||
<CdtDbtInd>CRDT</CdtDbtInd>
|
||||
<Dt>
|
||||
<Dt>2019-07-05</Dt>
|
||||
</Dt>
|
||||
</Bal>
|
||||
<TxsSummry>
|
||||
<TtlDbtNtries>
|
||||
<NbOfNtries>2</NbOfNtries>
|
||||
<Sum>24.70</Sum>
|
||||
</TtlDbtNtries>
|
||||
</TxsSummry>
|
||||
<Ntry>
|
||||
<NtryRef>1532133113</NtryRef>
|
||||
<Amt Ccy="CHF">15.45</Amt>
|
||||
<CdtDbtInd>DBIT</CdtDbtInd>
|
||||
<Sts>BOOK</Sts>
|
||||
<BookgDt>
|
||||
<Dt>2019-07-01</Dt>
|
||||
</BookgDt>
|
||||
<ValDt>
|
||||
<Dt>2019-06-28</Dt>
|
||||
</ValDt>
|
||||
<BkTxCd>
|
||||
<Domn>
|
||||
<Cd>PMNT</Cd>
|
||||
<Fmly>
|
||||
<Cd>*</Cd>
|
||||
<SubFmlyCd>*</SubFmlyCd>
|
||||
</Fmly>
|
||||
</Domn>
|
||||
</BkTxCd>
|
||||
<NtryDtls>
|
||||
<TxDtls>
|
||||
<Amt Ccy="CHF">15.45</Amt>
|
||||
<CdtDbtInd>DBIT</CdtDbtInd>
|
||||
<BkTxCd>
|
||||
<Domn>
|
||||
<Cd>PMNT</Cd>
|
||||
<Fmly>
|
||||
<Cd>*</Cd>
|
||||
<SubFmlyCd>*</SubFmlyCd>
|
||||
</Fmly>
|
||||
</Domn>
|
||||
</BkTxCd>
|
||||
<RltdPties/>
|
||||
<RmtInf>
|
||||
<Ustrd>Migros M EX Sihlpass</Ustrd>
|
||||
</RmtInf>
|
||||
<AddtlTxInf>Warenbezug und Dienstleistungen
|
||||
Migros M EX Sihlpass
|
||||
Maestro Karte
|
||||
Kartennummer xxx
|
||||
28.06.2019 18:43</AddtlTxInf>
|
||||
</TxDtls>
|
||||
</NtryDtls>
|
||||
<AddtlNtryInf>Warenbezug und Dienstleistungen
|
||||
Migros M EX Sihlpass
|
||||
Maestro Karte
|
||||
Kartennummer xxx
|
||||
28.06.2019 18:43</AddtlNtryInf>
|
||||
</Ntry>
|
||||
<Ntry>
|
||||
<NtryRef>1531481701</NtryRef>
|
||||
<Amt Ccy="CHF">9.25</Amt>
|
||||
<CdtDbtInd>DBIT</CdtDbtInd>
|
||||
<Sts>BOOK</Sts>
|
||||
<BookgDt>
|
||||
<Dt>2019-07-01</Dt>
|
||||
</BookgDt>
|
||||
<ValDt>
|
||||
<Dt>2019-06-28</Dt>
|
||||
</ValDt>
|
||||
<BkTxCd>
|
||||
<Domn>
|
||||
<Cd>PMNT</Cd>
|
||||
<Fmly>
|
||||
<Cd>*</Cd>
|
||||
<SubFmlyCd>*</SubFmlyCd>
|
||||
</Fmly>
|
||||
</Domn>
|
||||
</BkTxCd>
|
||||
<NtryDtls>
|
||||
<TxDtls>
|
||||
<Amt Ccy="CHF">9.25</Amt>
|
||||
<CdtDbtInd>DBIT</CdtDbtInd>
|
||||
<BkTxCd>
|
||||
<Domn>
|
||||
<Cd>PMNT</Cd>
|
||||
<Fmly>
|
||||
<Cd>*</Cd>
|
||||
<SubFmlyCd>*</SubFmlyCd>
|
||||
</Fmly>
|
||||
</Domn>
|
||||
</BkTxCd>
|
||||
<RltdPties/>
|
||||
<RmtInf>
|
||||
<Ustrd>Coop-5013 ZH Maagpla</Ustrd>
|
||||
</RmtInf>
|
||||
<AddtlTxInf>Warenbezug und Dienstleistungen
|
||||
Coop-5013 ZH Maagpla
|
||||
Maestro Karte
|
||||
Kartennummer xxx
|
||||
28.06.2019 06:49</AddtlTxInf>
|
||||
</TxDtls>
|
||||
</NtryDtls>
|
||||
<AddtlNtryInf>Warenbezug und Dienstleistungen
|
||||
Coop-5013 ZH Maagpla
|
||||
Maestro Karte
|
||||
Kartennummer xxx
|
||||
28.06.2019 06:49</AddtlNtryInf>
|
||||
</Ntry>
|
||||
</Stmt>
|
||||
</BkToCstmrStmt>
|
||||
</Document>
|
||||
5
Schaad.Finance.Tests/Data/cembra.csv
Normal file
5
Schaad.Finance.Tests/Data/cembra.csv
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
Booking Date;Transaction Date;;Transaction;Type;Amount (CHF);
|
||||
05.07.2019;05.07.2019;;MIGROS WEINFELDEN;Debit;37.00;
|
||||
04.07.2019;04.07.2019;;MIGROS WEINFELDEN;Debit;46.95;
|
||||
02.07.2019;02.07.2019;;MIGROS WEINFELDEN;Debit;47.75;
|
||||
02.07.2019;02.07.2019;;ITUNES.COM/BILL 0800001853 IRL;Debit;12.90;
|
||||
|
47
Schaad.Finance.Tests/Data/mt940.sta
Normal file
47
Schaad.Finance.Tests/Data/mt940.sta
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
:20:STARTUMS
|
||||
:25:123456789
|
||||
:28:00001
|
||||
:NS:22 Schaad Claudio
|
||||
23Privatkonto
|
||||
3000000784
|
||||
314448809
|
||||
32000
|
||||
:60F:C190701CHF000000002468,71
|
||||
:61:1906280701D000000000015,45FMSCNONREF
|
||||
:NS:01Warenbezug und Dienstleistungen
|
||||
02Migros M EX Sihlpass
|
||||
03Maestro Karte
|
||||
04Kartennummer xxx
|
||||
0528.06.2019 18:43
|
||||
06
|
||||
07
|
||||
08
|
||||
09
|
||||
10
|
||||
11
|
||||
12
|
||||
13
|
||||
14
|
||||
15
|
||||
16
|
||||
17Warenbezug und Dienstleistungen
|
||||
:61:1906280701D000000000009,25FMSCNONREF
|
||||
:NS:01Warenbezug und Dienstleistungen
|
||||
02Coop-5013 ZH Maagpla
|
||||
03Maestro Karte
|
||||
04Kartennummer xxx
|
||||
0528.06.2019 06:49
|
||||
06
|
||||
07
|
||||
08
|
||||
09
|
||||
10
|
||||
11
|
||||
12
|
||||
13
|
||||
14
|
||||
15
|
||||
16
|
||||
17Warenbezug und Dienstleistungen
|
||||
:62F:C190705CHF000000002444,01
|
||||
35
Schaad.Finance.Tests/FxServiceTest.cs
Normal file
35
Schaad.Finance.Tests/FxServiceTest.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using NUnit.Framework;
|
||||
using Schaad.Finance.Services;
|
||||
|
||||
namespace Schaad.Finance.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class FxServiceTest
|
||||
{
|
||||
private FxService service;
|
||||
|
||||
private const string FIXERIO_API_KEY = "<insert fixer io api key here>";
|
||||
|
||||
[SetUp]
|
||||
public void Startup()
|
||||
{
|
||||
service = new FxService();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFxRate_EUR2CHF()
|
||||
{
|
||||
var fxRate = service.GetFxRate("EUR", "CHF", new System.DateTime(2019, 07, 01), FIXERIO_API_KEY);
|
||||
|
||||
Assert.That(fxRate, Is.EqualTo(1.114262m));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertCurrency_100EUR2CHF()
|
||||
{
|
||||
var amount = service.ConvertCurrency(100, "EUR", "CHF", FIXERIO_API_KEY);
|
||||
|
||||
Assert.That(amount, Is.GreaterThan(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Schaad.Finance.Tests/Schaad.Finance.Tests.csproj
Normal file
46
Schaad.Finance.Tests/Schaad.Finance.Tests.csproj
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Schaad.Finance\Schaad.Finance.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Data\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Schaad.Web">
|
||||
<HintPath>..\dlls\Schaad.Web.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Schaad.Web.Api">
|
||||
<HintPath>..\dlls\Schaad.Web.Api.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Data\camt053.xml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Data\cembra.csv">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Data\mt940.sta">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
37
Schaad.Finance.sln
Normal file
37
Schaad.Finance.sln
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27703.2035
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schaad.Finance.Api", "Schaad.Finance.Api\Schaad.Finance.Api.csproj", "{C5845C75-0772-4819-B0DB-D57B5AF8921D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Schaad.Finance", "Schaad.Finance\Schaad.Finance.csproj", "{4BCEDAF9-FE9B-49D2-9CAF-A7B06B4FF521}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schaad.Finance.Tests", "Schaad.Finance.Tests\Schaad.Finance.Tests.csproj", "{C3A9FC28-9CF7-4775-90B7-B9F29DC299FD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C5845C75-0772-4819-B0DB-D57B5AF8921D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C5845C75-0772-4819-B0DB-D57B5AF8921D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C5845C75-0772-4819-B0DB-D57B5AF8921D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C5845C75-0772-4819-B0DB-D57B5AF8921D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4BCEDAF9-FE9B-49D2-9CAF-A7B06B4FF521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4BCEDAF9-FE9B-49D2-9CAF-A7B06B4FF521}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4BCEDAF9-FE9B-49D2-9CAF-A7B06B4FF521}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4BCEDAF9-FE9B-49D2-9CAF-A7B06B4FF521}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C3A9FC28-9CF7-4775-90B7-B9F29DC299FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C3A9FC28-9CF7-4775-90B7-B9F29DC299FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C3A9FC28-9CF7-4775-90B7-B9F29DC299FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C3A9FC28-9CF7-4775-90B7-B9F29DC299FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {38CCE775-5C10-4F00-BA79-EB86BE0BB2B5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
164
Schaad.Finance/Formats/AccountStatements/CAMT053.cs
Normal file
164
Schaad.Finance/Formats/AccountStatements/CAMT053.cs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using Schaad.Finance.Api;
|
||||
using Schaad.Finance.Api.AccountStatements;
|
||||
using Schaad.Finance.Formats.AccountStatements.Models;
|
||||
|
||||
namespace Schaad.Finance.Formats.AccountStatements
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads CAMT053 format
|
||||
/// https://www.six-interbank-clearing.com/dam/downloads/de/standardization/iso/swiss-recommendations/implementation-guidelines-camt.pdf
|
||||
/// </summary>
|
||||
public class CAMT053 : IAccountStatementParsingService
|
||||
{
|
||||
public List<AccountStatement> ReadFile(string filePath, Encoding encoding)
|
||||
{
|
||||
var accountStatements = new List<AccountStatement>();
|
||||
var serializer = new XmlSerializer(typeof(Document));
|
||||
using (var fileStream = new FileStream(filePath, FileMode.Open))
|
||||
{
|
||||
using (var reader = new StreamReader(fileStream, encoding))
|
||||
{
|
||||
var document = (Document)serializer.Deserialize(reader);
|
||||
foreach (var stmt in document.BkToCstmrStmt.Stmt)
|
||||
{
|
||||
var accountStatement = new AccountStatement();
|
||||
accountStatements.Add(accountStatement);
|
||||
|
||||
accountStatement.AccountNumber = stmt.Acct.Id.Item.ToString();
|
||||
accountStatement.Currency = stmt.Acct.Ccy;
|
||||
|
||||
foreach (var balance in stmt.Bal)
|
||||
{
|
||||
// var balanceAmount = balance.Amt.Value;
|
||||
// var balanceCurrency = balance.Amt.Ccy;
|
||||
// var balanceDate = balance.Dt.Item;
|
||||
// var creditDebit = balance.CdtDbtInd;
|
||||
|
||||
var balanceType = (BalanceType12Code)balance.Tp.CdOrPrtry.Item;
|
||||
if (balanceType == BalanceType12Code.OPBD)
|
||||
{
|
||||
accountStatement.StartBalance = new Balance
|
||||
{
|
||||
BookingDate = balance.Dt.Item,
|
||||
Value = (double)balance.Amt.Value,
|
||||
};
|
||||
}
|
||||
else if (balanceType == BalanceType12Code.CLBD)
|
||||
{
|
||||
accountStatement.EndBalance = new Balance
|
||||
{
|
||||
BookingDate = balance.Dt.Item,
|
||||
Value = (double)balance.Amt.Value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (stmt.TxsSummry != null)
|
||||
{
|
||||
if (stmt.TxsSummry.TtlCdtNtries != null)
|
||||
{
|
||||
accountStatement.CreditTransactionSummary = new TransactionSummary
|
||||
{
|
||||
NumberOfTransaction = int.Parse(stmt.TxsSummry.TtlCdtNtries.NbOfNtries),
|
||||
Sum = stmt.TxsSummry.TtlCdtNtries.Sum
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
accountStatement.CreditTransactionSummary = new TransactionSummary();
|
||||
}
|
||||
|
||||
if (stmt.TxsSummry.TtlDbtNtries != null)
|
||||
{
|
||||
accountStatement.DebitTransactionSummary = new TransactionSummary
|
||||
{
|
||||
NumberOfTransaction = int.Parse(stmt.TxsSummry.TtlDbtNtries.NbOfNtries),
|
||||
Sum = stmt.TxsSummry.TtlDbtNtries.Sum
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
accountStatement.DebitTransactionSummary = new TransactionSummary();
|
||||
}
|
||||
}
|
||||
|
||||
accountStatement.Transactions = new List<Transaction>();
|
||||
if (stmt.Ntry != null)
|
||||
{
|
||||
foreach (var entry in stmt.Ntry)
|
||||
{
|
||||
var transaction = new Transaction();
|
||||
accountStatement.Transactions.Add(transaction);
|
||||
|
||||
// var amount = entry.Amt.Value;
|
||||
// var currency = entry.Amt.Ccy;
|
||||
// var valueDate = entry.ValDt.Item;
|
||||
// var bookingDate = entry.BookgDt.Item;
|
||||
//var creditDepit = entry.CdtDbtInd;
|
||||
// var trxId = entry.NtryRef;
|
||||
// var text = entry.AddtlNtryInf;
|
||||
|
||||
transaction.Id = stmt.Id + entry.NtryRef;
|
||||
transaction.BookingDate = entry.BookgDt.Item;
|
||||
transaction.Text = entry.AddtlNtryInf;
|
||||
transaction.Value = (double)entry.Amt.Value;
|
||||
transaction.ValueDate = entry.ValDt.Item;
|
||||
|
||||
if (entry.CdtDbtInd == CreditDebitCode.DBIT)
|
||||
{
|
||||
transaction.Value *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return accountStatements;
|
||||
}
|
||||
|
||||
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})";
|
||||
}
|
||||
|
||||
if (accountStatement.CreditTransactionSummary != null && accountStatement.DebitTransactionSummary != null)
|
||||
{
|
||||
var transactionSum = Math.Round(accountStatement.Transactions.Sum(t => (decimal)t.Value), 2);
|
||||
var totalNumber = accountStatement.Transactions.Count;
|
||||
|
||||
var transactionSumValidation = accountStatement.CreditTransactionSummary.Sum - accountStatement.DebitTransactionSummary.Sum;
|
||||
var totalNumberValidation = accountStatement.CreditTransactionSummary.NumberOfTransaction + accountStatement.DebitTransactionSummary.NumberOfTransaction;
|
||||
|
||||
if (transactionSum != transactionSumValidation)
|
||||
{
|
||||
return $"Transaction sum ({transactionSum}) does not match validation sum ({transactionSumValidation})";
|
||||
}
|
||||
|
||||
if (totalNumber != totalNumberValidation)
|
||||
{
|
||||
return $"Transaction count ({totalNumber}) does not match validation count ({totalNumberValidation})";
|
||||
}
|
||||
}
|
||||
else if (accountStatement.StartBalance != null && accountStatement.EndBalance != null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
195
Schaad.Finance/Formats/AccountStatements/MT940.cs
Normal file
195
Schaad.Finance/Formats/AccountStatements/MT940.cs
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads MT940 format
|
||||
/// http://www.kontopruef.de/mt940s.shtml
|
||||
/// </summary>
|
||||
public class MT940 : IAccountStatementParsingService
|
||||
{
|
||||
public List<AccountStatement> ReadFile(string filePath, Encoding encoding)
|
||||
{
|
||||
var accountList = new List<AccountStatement>();
|
||||
AccountStatement account = null;
|
||||
Transaction currentTransaction = null;
|
||||
List<string> 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<string>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a line
|
||||
/// </summary>
|
||||
/// <param name="line"></param>
|
||||
/// <returns></returns>
|
||||
private Tuple<string, string> ParseLine(string line)
|
||||
{
|
||||
if (line.StartsWith(":"))
|
||||
{
|
||||
var pos = line.IndexOf(':', 1);
|
||||
var code = line.Substring(0, pos + 1);
|
||||
return new Tuple<string, string>(code, line.Substring(code.Length));
|
||||
}
|
||||
else
|
||||
return new Tuple<string, string>(line.Substring(0, 2), line.Substring(2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a balance
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
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(',', '.'));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a transaction
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a date in format: JJMMTT
|
||||
/// </summary>
|
||||
/// <param name="dateValue"></param>
|
||||
/// <returns></returns>
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
11206
Schaad.Finance/Formats/AccountStatements/Models/camt_053_001_04.cs
Normal file
11206
Schaad.Finance/Formats/AccountStatements/Models/camt_053_001_04.cs
Normal file
File diff suppressed because it is too large
Load diff
1298
Schaad.Finance/Formats/AccountStatements/camt.053.001.02.xsd
Normal file
1298
Schaad.Finance/Formats/AccountStatements/camt.053.001.02.xsd
Normal file
File diff suppressed because it is too large
Load diff
1764
Schaad.Finance/Formats/AccountStatements/camt.053.001.04.xsd
Normal file
1764
Schaad.Finance/Formats/AccountStatements/camt.053.001.04.xsd
Normal file
File diff suppressed because it is too large
Load diff
17
Schaad.Finance/Formats/CreditCardProvider/CembraMap.cs
Normal file
17
Schaad.Finance/Formats/CreditCardProvider/CembraMap.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using CsvHelper.Configuration;
|
||||
using Schaad.Finance.Api.Datasets;
|
||||
|
||||
namespace Schaad.Finance.Formats.CreditCardProvider
|
||||
{
|
||||
class CembraMap : ClassMap<CreditCardTransaction>
|
||||
{
|
||||
public CembraMap()
|
||||
{
|
||||
Map(m => m.BookingDate).Name("Booking Date");
|
||||
Map(m => m.TransactionDate).Name("Transaction Date");
|
||||
Map(m => m.Transaction).Name("Transaction");
|
||||
Map(m => m.TransactionType).Name("Type");
|
||||
Map(m => m.Amount).Name("Amount (CHF)");
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Schaad.Finance/Schaad.Finance.csproj
Normal file
17
Schaad.Finance/Schaad.Finance.csproj
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CsvHelper" Version="12.1.2" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Schaad.Finance.Api\Schaad.Finance.Api.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
44
Schaad.Finance/Services/AccountStatementService.cs
Normal file
44
Schaad.Finance/Services/AccountStatementService.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Schaad.Finance.Api;
|
||||
using Schaad.Finance.Api.AccountStatements;
|
||||
using Schaad.Finance.Formats.AccountStatements;
|
||||
|
||||
namespace Schaad.Finance.Services
|
||||
{
|
||||
public class AccountStatementService : IAccountStatementService
|
||||
{
|
||||
public List<AccountStatementResult> ReadFile(string filePath, Encoding encoding)
|
||||
{
|
||||
IAccountStatementParsingService accountStatementParsingService = null;
|
||||
var extension = Path.GetExtension(filePath).ToLower();
|
||||
switch (extension)
|
||||
{
|
||||
case ".xml":
|
||||
accountStatementParsingService = new CAMT053() as IAccountStatementParsingService;
|
||||
break;
|
||||
|
||||
case ".sta":
|
||||
accountStatementParsingService = new MT940() as IAccountStatementParsingService;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknwon ending {extension}");
|
||||
}
|
||||
|
||||
var accountStatementResults = new List<AccountStatementResult>();
|
||||
var accountStatements = accountStatementParsingService.ReadFile(filePath, encoding);
|
||||
foreach (var accountStatement in accountStatements)
|
||||
{
|
||||
var error = accountStatementParsingService.ValidateAccountStatement(accountStatement);
|
||||
var accountStatementResult = new AccountStatementResult(accountStatement, error);
|
||||
accountStatementResults.Add(accountStatementResult);
|
||||
}
|
||||
|
||||
return accountStatementResults;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Schaad.Finance/Services/CreditCardStatementService.cs
Normal file
39
Schaad.Finance/Services/CreditCardStatementService.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CsvHelper;
|
||||
using Schaad.Finance.Api;
|
||||
using Schaad.Finance.Api.Datasets;
|
||||
|
||||
namespace Schaad.Finance.Services
|
||||
{
|
||||
public class CreditCardStatementService : ICreditCardStatementService
|
||||
{
|
||||
public IReadOnlyList<CreditCardTransaction> ReadFile(CreditCardProvider creditCardProvider, string filePath, Encoding encoding)
|
||||
{
|
||||
switch (creditCardProvider)
|
||||
{
|
||||
case CreditCardProvider.Cembra:
|
||||
return ParseCembra(filePath, encoding);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unnknown procider {creditCardProvider}");
|
||||
}
|
||||
}
|
||||
|
||||
private IReadOnlyList<CreditCardTransaction> ParseCembra(string filePath, Encoding encoding)
|
||||
{
|
||||
using (var reader = new StreamReader(filePath, encoding))
|
||||
{
|
||||
using (var csv = new CsvReader(reader))
|
||||
{
|
||||
csv.Configuration.RegisterClassMap<Formats.CreditCardProvider.CembraMap>();
|
||||
var records = csv.GetRecords<CreditCardTransaction>();
|
||||
return records.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
Schaad.Finance/Services/FxService.cs
Normal file
81
Schaad.Finance/Services/FxService.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using Schaad.Finance.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace Schaad.Finance.Services
|
||||
{
|
||||
public class FxService : IFxService
|
||||
{
|
||||
private Dictionary<string, decimal> fxRateCache;
|
||||
|
||||
public FxService()
|
||||
{
|
||||
fxRateCache = new Dictionary<string, decimal>();
|
||||
}
|
||||
|
||||
public decimal ConvertCurrency(decimal amount, string fromCurrency, string toCurrency, string fixerIoApiKey)
|
||||
{
|
||||
if (fromCurrency == toCurrency)
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
|
||||
var fxRate = GetFxRate(fromCurrency, toCurrency, DateTime.Now, fixerIoApiKey);
|
||||
return Math.Round(fxRate * amount, 2);
|
||||
}
|
||||
|
||||
public decimal GetFxRate(string fromCurrency, string toCurrency, DateTime date, string fixerIoApiKey)
|
||||
{
|
||||
if (fromCurrency == toCurrency)
|
||||
{
|
||||
return 1.0M;
|
||||
}
|
||||
|
||||
var key = GetKey(fromCurrency, toCurrency, date);
|
||||
if (fxRateCache.ContainsKey(key) == false)
|
||||
{
|
||||
// Base Currency ist EUR
|
||||
var euroToFrom = GetFx(GetUrl(fromCurrency, date, fixerIoApiKey), fromCurrency);
|
||||
var euroToTo = GetFx(GetUrl(toCurrency, date, fixerIoApiKey), toCurrency);
|
||||
|
||||
fxRateCache[key] = euroToTo / euroToFrom;
|
||||
}
|
||||
|
||||
return fxRateCache[key];
|
||||
}
|
||||
|
||||
private string GetUrl(string currency, DateTime date, string fixerIoApiKey)
|
||||
{
|
||||
var url = $"http://data.fixer.io/api/{date:yyyy-MM-dd}?access_key={fixerIoApiKey}&symbols={currency}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private decimal GetFx(string url, string currency)
|
||||
{
|
||||
var response = HttpGet(url);
|
||||
dynamic stuff = JObject.Parse(response);
|
||||
return stuff.rates[currency];
|
||||
}
|
||||
|
||||
private string GetKey(string fromCurrency, string toCurrency, DateTime date)
|
||||
{
|
||||
return $"{fromCurrency}_{toCurrency}_{date:yyyyMMdd}";
|
||||
}
|
||||
|
||||
private string HttpGet(string url)
|
||||
{
|
||||
var request = (HttpWebRequest)WebRequest.Create(url);
|
||||
var response = request.GetResponseAsync().Result;
|
||||
string myResponse = "";
|
||||
using (var sr = new System.IO.StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
myResponse = sr.ReadToEnd();
|
||||
return myResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in a new issue