diff --git a/Elwig/Helpers/Export/Ebics.cs b/Elwig/Helpers/Export/Ebics.cs index 8abb5b5..eeb15c7 100644 --- a/Elwig/Helpers/Export/Ebics.cs +++ b/Elwig/Helpers/Export/Ebics.cs @@ -2,12 +2,13 @@ using Elwig.Models.Dtos; using Elwig.Models.Entities; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; namespace Elwig.Helpers.Export { - public class Ebics(PaymentVar variant, string filename) : IBankingExporter { + public class Ebics(PaymentVar variant, string filename, int version) : IBankingExporter { public static string FileExtension => "xml"; @@ -16,6 +17,7 @@ namespace Elwig.Helpers.Export { private readonly int Year = variant.Year; private readonly string Name = variant.Name; private readonly int AvNr = variant.AvNr; + private readonly int Version = version; public void Dispose() { GC.SuppressFinalize(this); @@ -32,6 +34,8 @@ namespace Elwig.Helpers.Export { } public async Task ExportAsync(IEnumerable transactions, IProgress? progress = null) { + if (transactions.Any(tx => tx.Amount < 0)) + throw new ArgumentException("Tranaction amount may not be negative"); progress?.Report(0.0); var nbOfTxs = transactions.Count(); int count = nbOfTxs + 2, i = 0; @@ -41,9 +45,7 @@ namespace Elwig.Helpers.Export { await Writer.WriteLineAsync($""" - + {msgId} @@ -57,10 +59,10 @@ namespace Elwig.Helpers.Export { TRF {nbOfTxs} {Transaction.FormatAmount(ctrlSum)} -
{Date:yyyy-MM-dd}
+ {(Version >= 8 ? "
" : "")}{Date:yyyy-MM-dd}{(Version >= 8 ? "
" : "")}
{App.Client.NameFull} - {App.Client.Iban?.Replace(" ", "")} - {App.Client.Bic ?? "NOTPROVIDED"} + {App.Client.Iban!.Replace(" ", "")} + {(Version >= 4 ? "" : "")}{App.Client.Bic ?? "NOTPROVIDED"}{(Version >= 4 ? "" : "")} """); progress?.Report(100.0 * ++i / count); @@ -74,15 +76,14 @@ namespace Elwig.Helpers.Export { {id} {Transaction.FormatAmount(tx.Amount)} - {a.Name} + {a.Name[..Math.Min(140, a.Name.Length)]} - {a1}{a2} + {a1?[..Math.Min(70, a1.Length)]}{a2?[..Math.Min(16, a2.Length)]} {a.PostalDest.AtPlz?.Plz}{a.PostalDest.AtPlz?.Ort.Name} {a.PostalDest.Country.Alpha2} - {tx.Member.Iban} - {tx.Member.Bic ?? "NOTPROVIDED"} + {tx.Member.Iban!} {info} """); diff --git a/Elwig/Models/Dtos/Transaction.cs b/Elwig/Models/Dtos/Transaction.cs index 860bc39..4cac4bf 100644 --- a/Elwig/Models/Dtos/Transaction.cs +++ b/Elwig/Models/Dtos/Transaction.cs @@ -1,4 +1,5 @@ using Elwig.Models.Entities; +using System; using System.Collections.Generic; using System.Linq; @@ -19,7 +20,7 @@ namespace Elwig.Models.Dtos { .ToList(); } - public static string FormatAmountCent(long cents) => $"{cents / 100}.{cents % 100:00}"; + public static string FormatAmountCent(long cents) => $"{cents / 100}.{Math.Abs(cents % 100):00}"; public static string FormatAmount(decimal amount) => FormatAmountCent((int)(amount * 100)); } diff --git a/Elwig/Windows/PaymentVariantsWindow.xaml.cs b/Elwig/Windows/PaymentVariantsWindow.xaml.cs index 50d7dd4..479a775 100644 --- a/Elwig/Windows/PaymentVariantsWindow.xaml.cs +++ b/Elwig/Windows/PaymentVariantsWindow.xaml.cs @@ -307,8 +307,12 @@ namespace Elwig.Windows { if (d.ShowDialog() == true) { ExportButton.IsEnabled = false; Mouse.OverrideCursor = Cursors.AppStarting; - using var e = new Ebics(v, d.FileName); - await e.ExportAsync(Transaction.FromPaymentVariant(v)); + try { + using var e = new Ebics(v, d.FileName, 9); + await e.ExportAsync(Transaction.FromPaymentVariant(v)); + } catch (Exception exc) { + MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); + } Mouse.OverrideCursor = null; ExportButton.IsEnabled = true; } diff --git a/Tests/HelperTests/EbicsTest.cs b/Tests/HelperTests/EbicsTest.cs new file mode 100644 index 0000000..d84280b --- /dev/null +++ b/Tests/HelperTests/EbicsTest.cs @@ -0,0 +1,113 @@ +using Elwig.Helpers; +using Elwig.Helpers.Export; +using Elwig.Models.Dtos; +using Elwig.Models.Entities; +using System.Reflection; +using System.Xml; + +namespace Tests.HelperTests { + // see https://www.iso20022.org/iso-20022-message-definitions + // and https://www.iso20022.org/catalogue-messages/iso-20022-messages-archive?search=pain + [TestFixture] + public class EbicsTest { + + public static readonly string FileName = Path.Combine(Path.GetTempPath(), "test_ebics.xml"); + public static readonly string Iban = "AT123456789012345678"; + + private static void ValidateSchema(string xmlPath, int version) { + XmlDocument xml = new(); + xml.Load(xmlPath); + var schema = new XmlTextReader(Assembly.GetExecutingAssembly() + .GetManifestResourceStream($"Tests.Resources.Schemas.pain.001.001.{version:00}.xsd")!); + xml.Schemas.Add(null, schema); + xml.Validate(null); + } + + private static async Task CreateXmlFile(int version) { + var v = new PaymentVar() { + Year = 2020, + AvNr = 1, + Name = "Endauszahlung", + TransferDate = new DateOnly(2021, 6, 15), + }; + using var ctx = new AppDbContext(); + var members = ctx.Members.ToList(); + Assert.That(members, Has.Count.GreaterThan(0)); + using var exporter = new Ebics(v, FileName, version); + await exporter.ExportAsync(members.Select(m => new Transaction(m, 1234.56m, "EUR", m.MgNr % 100))); + } + + [TearDown] + public static void RemoveXmlFile() { + File.Delete(FileName); + } + + [Test] + [Ignore("Version has no need to be supported")] + public async Task Test_CustomerCreditTransferInitiationV01() { + await CreateXmlFile(1); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 1)); + } + + [Test] + [Ignore("Version has no need to be supported")] + public async Task Test_CustomerCreditTransferInitiationV02() { + await CreateXmlFile(2); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 2)); + } + + [Test] + public async Task Test_CustomerCreditTransferInitiationV03() { + await CreateXmlFile(3); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 3)); + } + + [Test] + public async Task Test_CustomerCreditTransferInitiationV04() { + await CreateXmlFile(4); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 4)); + } + + [Test] + public async Task Test_CustomerCreditTransferInitiationV05() { + await CreateXmlFile(5); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 5)); + } + + [Test] + public async Task Test_CustomerCreditTransferInitiationV06() { + await CreateXmlFile(6); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 6)); + } + + [Test] + public async Task Test_CustomerCreditTransferInitiationV07() { + await CreateXmlFile(7); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 7)); + } + + [Test] + public async Task Test_CustomerCreditTransferInitiationV08() { + await CreateXmlFile(8); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 8)); + } + + [Test] + public async Task Test_CustomerCreditTransferInitiationV09() { + await CreateXmlFile(9); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 9)); + } + + [Test] + public async Task Test_CustomerCreditTransferInitiationV10() { + await CreateXmlFile(10); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 10)); + } + + [Test] + public async Task Test_CustomerCreditTransferInitiationV11() { + await CreateXmlFile(11); + Assert.DoesNotThrow(() => ValidateSchema(FileName, 11)); + } + } +}