.gitea
Elwig
Controls
Dialogs
Documents
Helpers
Billing
Export
Bki.cs
Csv.cs
Ebics.cs
Elba.cs
ElwigData.cs
IBankingExporter.cs
IExporter.cs
Ods.cs
Printing
Weighing
ActionCommand.cs
AppDbContext.cs
AppDbUpdater.cs
ClientParameters.cs
Config.cs
ControlUtils.cs
ExportMode.cs
Extensions.cs
NullItem.cs
TempFile.cs
Utils.cs
Validator.cs
Models
Properties
Resources
Themes
Windows
App.xaml
App.xaml.cs
AssemblyInfo.cs
Elwig.csproj
app.manifest
fetch-resources.bat
Installer
Setup
Tests
.gitignore
Elwig.sln
114 lines
5.6 KiB
C#
114 lines
5.6 KiB
C#
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");
|
|
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.Name[..Math.Min(140, a.Name.Length)])}</Nm>
|
|
""");
|
|
if (ShowAddresses != AddressMode.Omit) {
|
|
var full = ShowAddresses == AddressMode.Full;
|
|
await Writer.WriteLineAsync($"""
|
|
<PstlAdr>
|
|
{(full ? $"<StrtNm>{SecurityElement.Escape(a1?[..Math.Min(70, a1.Length)])}</StrtNm> <BldgNb>{SecurityElement.Escape(a2?[..Math.Min(16, a2.Length)])}</BldgNb>" : $"<AdrLine>{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{(full ? "" : "--")}>
|
|
</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);
|
|
}
|
|
}
|
|
}
|