using Elwig.Models; using Elwig.Models.Dtos; using Elwig.Models.Entities; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security; using System.Threading.Tasks; namespace Elwig.Helpers.Export { public class Ebics(PaymentVar variant, string filename, int version, Ebics.AddressMode mode = Ebics.AddressMode.Full) : IBankingExporter { public enum AddressMode { Omit = 0, Lines = 1, Full = 2 } public static string FileExtension => "xml"; private readonly StreamWriter Writer = new(filename, false, Utils.UTF8); private readonly DateOnly Date = variant.TransferDate ?? throw new ArgumentException("TransferDate has to be set in PaymentVar"); private readonly int Year = variant.Year; private readonly string Name = variant.Name; private readonly int AvNr = variant.AvNr; private readonly int Version = version; private readonly AddressMode ShowAddresses = mode; public void Dispose() { GC.SuppressFinalize(this); Writer.Dispose(); } public ValueTask DisposeAsync() { GC.SuppressFinalize(this); return Writer.DisposeAsync(); } public void Export(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) { ExportAsync(transactions, progress).GetAwaiter().GetResult(); } public async Task ExportAsync(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) { if (transactions.Any(tx => tx.Amount < 0)) { throw new ArgumentException("Tranaction amount may not be negative"); } else if (App.Client.Iban == null) { throw new ArgumentException("Client IBAN has to be set"); } progress?.Report(0.0); var nbOfTxs = transactions.Count(); int count = nbOfTxs + 2, i = 0; var ctrlSum = transactions.Sum(tx => tx.Amount); var msgId = $"ELWIG-{App.Client.NameToken}-{Year}-AV{AvNr:00}"; var pmtInfId = $"{msgId}-1"; await Writer.WriteLineAsync($""" <?xml version="1.0" encoding="UTF-8"?> <Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.{Version:00}"> <CstmrCdtTrfInitn> <GrpHdr> <MsgId>{msgId}</MsgId> <CreDtTm>{DateTime.UtcNow:o}</CreDtTm> <NbOfTxs>{nbOfTxs}</NbOfTxs> <CtrlSum>{Transaction.FormatAmount(ctrlSum)}</CtrlSum> <InitgPty><Nm>{SecurityElement.Escape(App.Client.NameFull)}</Nm></InitgPty> </GrpHdr> <PmtInf> <PmtInfId>{pmtInfId}</PmtInfId> <PmtMtd>TRF</PmtMtd> <NbOfTxs>{nbOfTxs}</NbOfTxs> <CtrlSum>{Transaction.FormatAmount(ctrlSum)}</CtrlSum> <ReqdExctnDt>{(Version >= 8 ? "<Dt>" : "")}{Date:yyyy-MM-dd}{(Version >= 8 ? "</Dt>" : "")}</ReqdExctnDt> <Dbtr><Nm>{SecurityElement.Escape(App.Client.NameFull)}</Nm></Dbtr> <DbtrAcct><Id><IBAN>{App.Client.Iban!.Replace(" ", "")}</IBAN></Id></DbtrAcct> <DbtrAgt><FinInstnId>{(Version >= 4 ? "<BICFI>" : "<BIC>")}{App.Client.Bic ?? "NOTPROVIDED"}{(Version >= 4 ? "</BICFI>" : "</BIC>")}</FinInstnId></DbtrAgt> """); progress?.Report(100.0 * ++i / count); foreach (var tx in transactions) { var a = (IAddress?)tx.Member.BillingAddress ?? tx.Member; var (a1, a2) = Utils.SplitAddress(a.Address); var id = $"ELWIG-{App.Client.NameToken}-{Year}-TG{tx.Nr:0000}"; var info = $"{Name} - Traubengutschrift Nr. {Year}/{tx.Nr:000}"; await Writer.WriteLineAsync($""" <CdtTrfTxInf> <PmtId><EndToEndId>{id}</EndToEndId></PmtId> <Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmount(tx.Amount)}</InstdAmt></Amt> <Cdtr> <Nm>{SecurityElement.Escape(a.FullName[..Math.Min(140, a.FullName.Length)])}</Nm> """); if (ShowAddresses != AddressMode.Omit) { var full = ShowAddresses == AddressMode.Full; await Writer.WriteLineAsync($""" <PstlAdr>{(full ? "" : $"\r\n <Ctry>{a.PostalDest.Country.Alpha2}</Ctry>")} {(full ? $"<StrtNm>{SecurityElement.Escape(a1?[..Math.Min(70, a1.Length)])}</StrtNm> <BldgNb>{SecurityElement.Escape(a2?[..Math.Min(16, a2.Length)])}</BldgNb>" : $"<AdrLine>{SecurityElement.Escape(a.Address[..Math.Min(70, a.Address.Length)])}</AdrLine>")} <{(full ? "PstCd" : "AdrLine")}>{a.PostalDest.AtPlz?.Plz}{(full ? "</PstCd> <TwnNm>" : " ")}{SecurityElement.Escape(a.PostalDest.AtPlz?.Ort.Name)}</{(full ? "TwnNm" : "AdrLine")}> {(full ? $" <Ctry>{a.PostalDest.Country.Alpha2}</Ctry>\r\n " : "")}</PstlAdr> """); } await Writer.WriteLineAsync($""" </Cdtr> <CdtrAcct><Id><IBAN>{tx.Member.Iban!}</IBAN></Id></CdtrAcct> <RmtInf><Ustrd>{SecurityElement.Escape(info)}</Ustrd></RmtInf> </CdtTrfTxInf> """); progress?.Report(100.0 * ++i / count); } await Writer.WriteLineAsync(""" </PmtInf> </CstmrCdtTrfInitn> </Document> """); await Writer.FlushAsync(); progress?.Report(100.0); } } }