P1c+P4: Module und Tests auf net8.0 - WinForms nur noch in PolyTrader.App
Variante B (Entscheidung Richard): Modul-UI entfernt statt in Zwischenprojekte ausgelagert. Avalonia ist plattformuebergreifend, die neuen Ansichten kommen spaeter direkt in die Modul-Projekte zurueck - kein Zwischenschritt, keine Wegwerfarbeit. - 23 WinForms-Dateien aus den 4 Modulen entfernt (Spezifikation steht in docs/UI-SPEZIFIKATION-WinForms.md, Originalcode im Tag winforms-final). - RegisterUi ist jetzt je Modul ein dokumentierter No-Op: View-ID, Titel, Gruppe, Order und der Tab-Aufbau stehen als XML-Doku drin, damit der Avalonia-Nachbau die stabilen IDs und die Struktur uebernimmt. - Alle 4 Modulprojekte + Testprojekt: net8.0 statt net8.0-windows, UseWindowsForms raus. - P4 vorgezogen (war durch den Testprojekt-Wechsel faellig): PDFsharp-MigraDoc-GDI -> PDFsharp-MigraDoc (Core-Build). Der Core-Build findet keine Systemschriften, daher neu Logic/PdfFontResolver.cs: durchsucht die Schriftverzeichnisse des OS nach Segoe UI/DejaVu/Liberation/Noto/Arial/FreeSans. Keine Schriftdateien im Repo noetig; fehlt auf Linux alles, kommt eine klare Meldung mit apt-Hinweis statt eines kryptischen Renderer-Fehlers. Verifiziert: Core, alle 4 Module und das Testprojekt publishen fuer linux-x64, und zwar ohne ein einziges Windows-spezifisches Paket in den deps.json. 442 Tests gruen (inkl. PDF-Rendering) auf net8.0. Windows-App laeuft weiter mit den Core-Fenstern. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -49,21 +49,22 @@ namespace PolyTrader.Modules.Accounting
|
||||
services.AddHostedService(sp => sp.GetRequiredService<AccountingIngestService>());
|
||||
}
|
||||
|
||||
public void RegisterUi(IModuleUiHost host, System.IServiceProvider services)
|
||||
/// <summary>
|
||||
/// Aktuell ohne Ansicht: Die WinForms-UI wurde mit der Linux-Portierung entfernt, die
|
||||
/// Avalonia-Ansicht folgt (Stufe L3/L4). Die Fachlogik dieses Moduls laeuft davon
|
||||
/// unabhaengig weiter – die Shell zeigt schlicht kein Fenster fuer das Modul an.
|
||||
///
|
||||
/// <para><b>Beim Nachbau zu erhalten</b> (Spezifikation: docs/UI-SPEZIFIKATION-WinForms.md,
|
||||
/// Originalcode: Git-Tag <c>winforms-final</c>):</para>
|
||||
/// <list type="bullet">
|
||||
/// <item>View-ID <c>accounting.main</c> (stabil – Launcher-Button und Symbol haengen daran)</item>
|
||||
/// <item>Titel <c>Accounting</c>, Gruppe <c>Accounting</c>, Order <c>400</c></item>
|
||||
/// <item>Ein Fenster fuers ganze Modul: AccountingMainForm mit Tabs: Uebersicht+BWA / Ledger / Abruf+Status</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public void RegisterUi(IModuleUiHost host, IServiceProvider services)
|
||||
{
|
||||
host.RegisterView(new ModuleView
|
||||
{
|
||||
Id = "accounting.main",
|
||||
Title = "Accounting",
|
||||
Group = "Accounting",
|
||||
Order = 400,
|
||||
CreateView = () =>
|
||||
{
|
||||
var form = new Ui.AccountingMainForm();
|
||||
form.Initialize(services);
|
||||
return form;
|
||||
}
|
||||
});
|
||||
// Bewusst leer, bis die Avalonia-Ansicht steht (siehe Doku oben).
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -28,9 +28,15 @@ namespace PolyTrader.Modules.Accounting.Logic
|
||||
decimal V(decimal usdc) => Math.Round(usdc * currencyFactor, 2, MidpointRounding.AwayFromZero);
|
||||
string M(decimal usdc) => V(usdc).ToString("N2") + " " + currencyCode;
|
||||
|
||||
// Schriftauflösung sicherstellen: der PDFsharp-Core-Build findet ohne Resolver keine
|
||||
// Systemschriften (siehe PdfFontResolver). Idempotent, kostet nach dem ersten Mal nichts.
|
||||
PdfFontResolver.EnsureRegistered();
|
||||
|
||||
var doc = new Document();
|
||||
doc.Info.Title = "Buchhalterische Abrechnung";
|
||||
var style = doc.Styles["Normal"];
|
||||
// Wunschschrift; der Resolver weicht auf eine verfügbare aus, wenn es sie nicht gibt
|
||||
// (auf Linux z.B. DejaVu Sans).
|
||||
style.Font.Name = "Segoe UI";
|
||||
style.Font.Size = 9;
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using PdfSharp.Fonts;
|
||||
|
||||
namespace PolyTrader.Modules.Accounting.Logic
|
||||
{
|
||||
/// <summary>
|
||||
/// Schriftauflösung für den PDF-Export. Nötig, seit der plattformneutrale PDFsharp-Core-Build
|
||||
/// verwendet wird: der frühere <c>-GDI</c>-Build zog Systemschriften über
|
||||
/// <c>System.Drawing.Common</c>, das seit .NET 7 Windows-only ist. Der Core-Build bringt keine
|
||||
/// eigene Schriftsuche mit und braucht deshalb diesen Resolver.
|
||||
///
|
||||
/// <para>Der Resolver durchsucht die üblichen Schriftverzeichnisse des Betriebssystems nach einer
|
||||
/// Kandidatenliste (Windows: Segoe UI/Arial, Linux: DejaVu/Liberation/Noto). Damit sind keine
|
||||
/// Schriftdateien im Repository nötig und die Lizenzfrage der Schriften bleibt beim System.</para>
|
||||
///
|
||||
/// <para><b>Voraussetzung auf Linux:</b> mindestens eine der Kandidatenschriften muss installiert
|
||||
/// sein. Auf Desktop-Distributionen ist das der Normalfall; auf schlanken Servern/Containern
|
||||
/// genügt <c>apt install fonts-dejavu-core</c> (bzw. <c>fonts-liberation</c>). Fehlt jede Schrift,
|
||||
/// wirft <see cref="EnsureRegistered"/> beim ersten Export eine Meldung mit genau diesem Hinweis –
|
||||
/// besser als eine kryptische Meldung aus dem Renderer-Inneren.</para>
|
||||
/// </summary>
|
||||
public sealed class PdfFontResolver : IFontResolver
|
||||
{
|
||||
/// <summary>Bevorzugte Schriftfamilien in Reihenfolge – erste gefundene gewinnt.</summary>
|
||||
private static readonly string[] PreferredFamilies =
|
||||
{
|
||||
"Segoe UI", "DejaVu Sans", "Liberation Sans", "Noto Sans", "Arial", "FreeSans"
|
||||
};
|
||||
|
||||
/// <summary>Dateinamens-Kandidaten je Familie und Schnitt (klein geschrieben, ohne Endung).</summary>
|
||||
private static readonly Dictionary<string, (string Regular, string Bold)> FileNames = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["Segoe UI"] = ("segoeui", "segoeuib"),
|
||||
["DejaVu Sans"] = ("DejaVuSans", "DejaVuSans-Bold"),
|
||||
["Liberation Sans"] = ("LiberationSans-Regular", "LiberationSans-Bold"),
|
||||
["Noto Sans"] = ("NotoSans-Regular", "NotoSans-Bold"),
|
||||
["Arial"] = ("arial", "arialbd"),
|
||||
["FreeSans"] = ("FreeSans", "FreeSansBold"),
|
||||
};
|
||||
|
||||
private static readonly ConcurrentDictionary<string, byte[]> Cache = new();
|
||||
private static readonly object RegisterLock = new();
|
||||
private static bool _registered;
|
||||
|
||||
/// <summary>
|
||||
/// Registriert den Resolver einmalig global. Muss vor dem ersten Rendern laufen; mehrfache
|
||||
/// Aufrufe sind unschädlich (PDFsharp erlaubt nur eine Zuweisung pro Prozess).
|
||||
/// </summary>
|
||||
public static void EnsureRegistered()
|
||||
{
|
||||
if (_registered) return;
|
||||
lock (RegisterLock)
|
||||
{
|
||||
if (_registered) return;
|
||||
if (FindAnyAvailableFamily() == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Keine geeignete Schriftart gefunden. Der PDF-Export braucht mindestens eine von: " +
|
||||
string.Join(", ", PreferredFamilies) + ". " +
|
||||
"Auf schlanken Linux-Systemen nachinstallieren, z.B. 'apt install fonts-dejavu-core'.");
|
||||
}
|
||||
GlobalFontSettings.FontResolver = new PdfFontResolver();
|
||||
_registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Erste verfügbare Familie aus <see cref="PreferredFamilies"/>, sonst <c>null</c>.</summary>
|
||||
internal static string? FindAnyAvailableFamily() =>
|
||||
PreferredFamilies.FirstOrDefault(f => LocateFile(f, bold: false) != null);
|
||||
|
||||
public FontResolverInfo? ResolveTypeface(string familyName, bool isBold, bool isItalic)
|
||||
{
|
||||
// Angeforderte Familie bevorzugen, sonst die erste verfügbare (z.B. "Segoe UI" auf Linux).
|
||||
string? family = LocateFile(familyName, isBold) != null ? familyName : FindAnyAvailableFamily();
|
||||
if (family == null) return null;
|
||||
|
||||
// Kursiv wird bewusst nicht aufgelöst: der Bericht nutzt es nicht, und ein fehlender
|
||||
// Kursiv-Schnitt wäre sonst ein harter Fehler statt einer kleinen Abweichung.
|
||||
return new FontResolverInfo($"{family}|{(isBold ? "b" : "r")}");
|
||||
}
|
||||
|
||||
public byte[]? GetFont(string faceName)
|
||||
{
|
||||
return Cache.GetOrAdd(faceName, key =>
|
||||
{
|
||||
var parts = key.Split('|');
|
||||
string family = parts[0];
|
||||
bool bold = parts.Length > 1 && parts[1] == "b";
|
||||
|
||||
string? path = LocateFile(family, bold) ?? LocateFile(family, bold: false);
|
||||
return path != null ? File.ReadAllBytes(path) : Array.Empty<byte>();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Sucht die Datei zu Familie + Schnitt in allen Schriftverzeichnissen des Systems.</summary>
|
||||
private static string? LocateFile(string family, bool bold)
|
||||
{
|
||||
if (!FileNames.TryGetValue(family, out var names)) return null;
|
||||
string stem = bold ? names.Bold : names.Regular;
|
||||
|
||||
foreach (string dir in FontDirectories())
|
||||
{
|
||||
if (!Directory.Exists(dir)) continue;
|
||||
foreach (string ext in new[] { ".ttf", ".otf" })
|
||||
{
|
||||
try
|
||||
{
|
||||
// Rekursiv, weil Linux die Schriften in Unterordnern ablegt (truetype/dejavu/…).
|
||||
var hit = Directory.EnumerateFiles(dir, stem + ext, SearchOption.AllDirectories)
|
||||
.FirstOrDefault();
|
||||
if (hit != null) return hit;
|
||||
}
|
||||
catch (UnauthorizedAccessException) { /* einzelne Ordner ohne Zugriff überspringen */ }
|
||||
catch (IOException) { /* z.B. defekter Symlink */ }
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Schriftverzeichnisse je Plattform.</summary>
|
||||
private static IEnumerable<string> FontDirectories()
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
yield return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts");
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
yield return "/System/Library/Fonts";
|
||||
yield return "/Library/Fonts";
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Linux/Unix
|
||||
yield return "/usr/share/fonts";
|
||||
yield return "/usr/local/share/fonts";
|
||||
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
if (!string.IsNullOrEmpty(home))
|
||||
{
|
||||
yield return Path.Combine(home, ".fonts");
|
||||
yield return Path.Combine(home, ".local", "share", "fonts");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PolyTrader.Core\PolyTrader.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="PDFsharp-MigraDoc-GDI" Version="6.2.4" />
|
||||
<!-- Core-Build statt -GDI: der GDI-Build braucht System.Drawing.Common und ist damit
|
||||
Windows-only. Der Core-Build findet dafuer keine Systemschriften von selbst -
|
||||
siehe Logic/PdfFontResolver.cs. -->
|
||||
<PackageReference Include="PDFsharp-MigraDoc" Version="6.2.4" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -21,11 +24,12 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<!-- Linux-Portierung: plattformneutral. Die WinForms-UI dieses Moduls (Ingest, BWA, Export)
|
||||
wurde entfernt; die Avalonia-Ansicht kommt spaeter direkt hierher zurueck – Avalonia ist
|
||||
im Gegensatz zu WinForms plattformuebergreifend und braucht kein eigenes UI-Projekt. -->
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- Modul trägt eigene WinForms-UI bei (Ledger/Status; BWA/Steuer/Export folgen A-2..A-4). -->
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,609 +0,0 @@
|
||||
namespace PolyTrader.Modules.Accounting.Ui
|
||||
{
|
||||
partial class AccountingMainForm
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Vom Komponenten-Designer generierter Code
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AccountingMainForm));
|
||||
tabControlAcc = new TabControl();
|
||||
tabUebersicht = new TabPage();
|
||||
dgvMonthly = new DataGridView();
|
||||
flpKpis = new FlowLayoutPanel();
|
||||
lblKpiNet = new Label();
|
||||
lblKpiClosing = new Label();
|
||||
lblKpiDeposits = new Label();
|
||||
lblKpiWithdrawals = new Label();
|
||||
lblKpiFees = new Label();
|
||||
lblKpiRewards = new Label();
|
||||
lblKpiVolume = new Label();
|
||||
lblKpiTrades = new Label();
|
||||
pnlOverviewTop = new Panel();
|
||||
btnExportPdf = new Button();
|
||||
btnExportCsv = new Button();
|
||||
btnCalc = new Button();
|
||||
cbCurrency = new ComboBox();
|
||||
lblWaehrung = new Label();
|
||||
dtTo = new DateTimePicker();
|
||||
lblBis = new Label();
|
||||
dtFrom = new DateTimePicker();
|
||||
lblVon = new Label();
|
||||
cbOvAccount = new ComboBox();
|
||||
lblOvKonto = new Label();
|
||||
tabLedger = new TabPage();
|
||||
dgvLedger = new DataGridView();
|
||||
toolStripLedger = new ToolStrip();
|
||||
lblLedgerKonto = new ToolStripLabel();
|
||||
cbLedgerAccount = new ToolStripComboBox();
|
||||
sepLedger = new ToolStripSeparator();
|
||||
btnLedgerRefresh = new ToolStripButton();
|
||||
tabStatus = new TabPage();
|
||||
dgvRuns = new DataGridView();
|
||||
toolStripStatus = new ToolStrip();
|
||||
lblStatusKonto = new ToolStripLabel();
|
||||
cbStatusAccount = new ToolStripComboBox();
|
||||
sepStatus = new ToolStripSeparator();
|
||||
btnIncremental = new ToolStripButton();
|
||||
btnBackfill = new ToolStripButton();
|
||||
sepStatus2 = new ToolStripSeparator();
|
||||
btnStatusRefresh = new ToolStripButton();
|
||||
lblAccStatus = new Label();
|
||||
tabControlAcc.SuspendLayout();
|
||||
tabUebersicht.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)dgvMonthly).BeginInit();
|
||||
flpKpis.SuspendLayout();
|
||||
pnlOverviewTop.SuspendLayout();
|
||||
tabLedger.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)dgvLedger).BeginInit();
|
||||
toolStripLedger.SuspendLayout();
|
||||
tabStatus.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)dgvRuns).BeginInit();
|
||||
toolStripStatus.SuspendLayout();
|
||||
SuspendLayout();
|
||||
//
|
||||
// tabControlAcc
|
||||
//
|
||||
tabControlAcc.Controls.Add(tabUebersicht);
|
||||
tabControlAcc.Controls.Add(tabLedger);
|
||||
tabControlAcc.Controls.Add(tabStatus);
|
||||
tabControlAcc.Dock = DockStyle.Fill;
|
||||
tabControlAcc.Location = new Point(0, 0);
|
||||
tabControlAcc.Margin = new Padding(4, 5, 4, 5);
|
||||
tabControlAcc.Name = "tabControlAcc";
|
||||
tabControlAcc.SelectedIndex = 0;
|
||||
tabControlAcc.Size = new Size(1571, 1030);
|
||||
tabControlAcc.TabIndex = 0;
|
||||
//
|
||||
// tabUebersicht
|
||||
//
|
||||
tabUebersicht.Controls.Add(dgvMonthly);
|
||||
tabUebersicht.Controls.Add(flpKpis);
|
||||
tabUebersicht.Controls.Add(pnlOverviewTop);
|
||||
tabUebersicht.Location = new Point(4, 34);
|
||||
tabUebersicht.Margin = new Padding(4, 5, 4, 5);
|
||||
tabUebersicht.Name = "tabUebersicht";
|
||||
tabUebersicht.Padding = new Padding(4, 5, 4, 5);
|
||||
tabUebersicht.Size = new Size(1563, 992);
|
||||
tabUebersicht.TabIndex = 0;
|
||||
tabUebersicht.Text = "Übersicht / BWA";
|
||||
tabUebersicht.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// dgvMonthly
|
||||
//
|
||||
dgvMonthly.AllowUserToAddRows = false;
|
||||
dgvMonthly.AllowUserToDeleteRows = false;
|
||||
dgvMonthly.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
dgvMonthly.Dock = DockStyle.Fill;
|
||||
dgvMonthly.Location = new Point(4, 222);
|
||||
dgvMonthly.Margin = new Padding(4, 5, 4, 5);
|
||||
dgvMonthly.Name = "dgvMonthly";
|
||||
dgvMonthly.ReadOnly = true;
|
||||
dgvMonthly.RowHeadersVisible = false;
|
||||
dgvMonthly.RowHeadersWidth = 62;
|
||||
dgvMonthly.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
||||
dgvMonthly.Size = new Size(1555, 765);
|
||||
dgvMonthly.TabIndex = 2;
|
||||
//
|
||||
// flpKpis
|
||||
//
|
||||
flpKpis.Controls.Add(lblKpiNet);
|
||||
flpKpis.Controls.Add(lblKpiClosing);
|
||||
flpKpis.Controls.Add(lblKpiDeposits);
|
||||
flpKpis.Controls.Add(lblKpiWithdrawals);
|
||||
flpKpis.Controls.Add(lblKpiFees);
|
||||
flpKpis.Controls.Add(lblKpiRewards);
|
||||
flpKpis.Controls.Add(lblKpiVolume);
|
||||
flpKpis.Controls.Add(lblKpiTrades);
|
||||
flpKpis.Dock = DockStyle.Top;
|
||||
flpKpis.Location = new Point(4, 65);
|
||||
flpKpis.Margin = new Padding(4, 5, 4, 5);
|
||||
flpKpis.Name = "flpKpis";
|
||||
flpKpis.Padding = new Padding(6, 7, 6, 7);
|
||||
flpKpis.Size = new Size(1555, 157);
|
||||
flpKpis.TabIndex = 1;
|
||||
//
|
||||
// lblKpiNet
|
||||
//
|
||||
lblKpiNet.BorderStyle = BorderStyle.FixedSingle;
|
||||
lblKpiNet.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
|
||||
lblKpiNet.Location = new Point(12, 14);
|
||||
lblKpiNet.Margin = new Padding(6, 7, 6, 7);
|
||||
lblKpiNet.MinimumSize = new Size(285, 122);
|
||||
lblKpiNet.Name = "lblKpiNet";
|
||||
lblKpiNet.Padding = new Padding(11, 13, 11, 13);
|
||||
lblKpiNet.Size = new Size(285, 122);
|
||||
lblKpiNet.TabIndex = 0;
|
||||
lblKpiNet.Text = "Netto-Handelsergebnis\n—";
|
||||
lblKpiNet.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// lblKpiClosing
|
||||
//
|
||||
lblKpiClosing.BorderStyle = BorderStyle.FixedSingle;
|
||||
lblKpiClosing.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
|
||||
lblKpiClosing.Location = new Point(309, 14);
|
||||
lblKpiClosing.Margin = new Padding(6, 7, 6, 7);
|
||||
lblKpiClosing.MinimumSize = new Size(256, 122);
|
||||
lblKpiClosing.Name = "lblKpiClosing";
|
||||
lblKpiClosing.Padding = new Padding(11, 13, 11, 13);
|
||||
lblKpiClosing.Size = new Size(256, 122);
|
||||
lblKpiClosing.TabIndex = 1;
|
||||
lblKpiClosing.Text = "Endsaldo\n—";
|
||||
lblKpiClosing.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// lblKpiDeposits
|
||||
//
|
||||
lblKpiDeposits.BorderStyle = BorderStyle.FixedSingle;
|
||||
lblKpiDeposits.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
|
||||
lblKpiDeposits.Location = new Point(577, 14);
|
||||
lblKpiDeposits.Margin = new Padding(6, 7, 6, 7);
|
||||
lblKpiDeposits.MinimumSize = new Size(213, 122);
|
||||
lblKpiDeposits.Name = "lblKpiDeposits";
|
||||
lblKpiDeposits.Padding = new Padding(11, 13, 11, 13);
|
||||
lblKpiDeposits.Size = new Size(213, 122);
|
||||
lblKpiDeposits.TabIndex = 2;
|
||||
lblKpiDeposits.Text = "Einzahlungen\n—";
|
||||
lblKpiDeposits.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// lblKpiWithdrawals
|
||||
//
|
||||
lblKpiWithdrawals.BorderStyle = BorderStyle.FixedSingle;
|
||||
lblKpiWithdrawals.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
|
||||
lblKpiWithdrawals.Location = new Point(802, 14);
|
||||
lblKpiWithdrawals.Margin = new Padding(6, 7, 6, 7);
|
||||
lblKpiWithdrawals.MinimumSize = new Size(213, 122);
|
||||
lblKpiWithdrawals.Name = "lblKpiWithdrawals";
|
||||
lblKpiWithdrawals.Padding = new Padding(11, 13, 11, 13);
|
||||
lblKpiWithdrawals.Size = new Size(213, 122);
|
||||
lblKpiWithdrawals.TabIndex = 3;
|
||||
lblKpiWithdrawals.Text = "Auszahlungen\n—";
|
||||
lblKpiWithdrawals.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// lblKpiFees
|
||||
//
|
||||
lblKpiFees.BorderStyle = BorderStyle.FixedSingle;
|
||||
lblKpiFees.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
|
||||
lblKpiFees.Location = new Point(1027, 14);
|
||||
lblKpiFees.Margin = new Padding(6, 7, 6, 7);
|
||||
lblKpiFees.MinimumSize = new Size(199, 122);
|
||||
lblKpiFees.Name = "lblKpiFees";
|
||||
lblKpiFees.Padding = new Padding(11, 13, 11, 13);
|
||||
lblKpiFees.Size = new Size(199, 122);
|
||||
lblKpiFees.TabIndex = 4;
|
||||
lblKpiFees.Text = "Fees\n—";
|
||||
lblKpiFees.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// lblKpiRewards
|
||||
//
|
||||
lblKpiRewards.BorderStyle = BorderStyle.FixedSingle;
|
||||
lblKpiRewards.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
|
||||
lblKpiRewards.Location = new Point(1238, 14);
|
||||
lblKpiRewards.Margin = new Padding(6, 7, 6, 7);
|
||||
lblKpiRewards.MinimumSize = new Size(199, 122);
|
||||
lblKpiRewards.Name = "lblKpiRewards";
|
||||
lblKpiRewards.Padding = new Padding(11, 13, 11, 13);
|
||||
lblKpiRewards.Size = new Size(199, 122);
|
||||
lblKpiRewards.TabIndex = 5;
|
||||
lblKpiRewards.Text = "Rewards\n—";
|
||||
lblKpiRewards.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// lblKpiVolume
|
||||
//
|
||||
lblKpiVolume.BorderStyle = BorderStyle.FixedSingle;
|
||||
lblKpiVolume.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
|
||||
lblKpiVolume.Location = new Point(12, 150);
|
||||
lblKpiVolume.Margin = new Padding(6, 7, 6, 7);
|
||||
lblKpiVolume.MinimumSize = new Size(228, 122);
|
||||
lblKpiVolume.Name = "lblKpiVolume";
|
||||
lblKpiVolume.Padding = new Padding(11, 13, 11, 13);
|
||||
lblKpiVolume.Size = new Size(228, 122);
|
||||
lblKpiVolume.TabIndex = 6;
|
||||
lblKpiVolume.Text = "Handelsvolumen\n—";
|
||||
lblKpiVolume.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// lblKpiTrades
|
||||
//
|
||||
lblKpiTrades.BorderStyle = BorderStyle.FixedSingle;
|
||||
lblKpiTrades.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
|
||||
lblKpiTrades.Location = new Point(252, 150);
|
||||
lblKpiTrades.Margin = new Padding(6, 7, 6, 7);
|
||||
lblKpiTrades.MinimumSize = new Size(171, 122);
|
||||
lblKpiTrades.Name = "lblKpiTrades";
|
||||
lblKpiTrades.Padding = new Padding(11, 13, 11, 13);
|
||||
lblKpiTrades.Size = new Size(171, 122);
|
||||
lblKpiTrades.TabIndex = 7;
|
||||
lblKpiTrades.Text = "Trades\n—";
|
||||
lblKpiTrades.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// pnlOverviewTop
|
||||
//
|
||||
pnlOverviewTop.Controls.Add(btnExportPdf);
|
||||
pnlOverviewTop.Controls.Add(btnExportCsv);
|
||||
pnlOverviewTop.Controls.Add(btnCalc);
|
||||
pnlOverviewTop.Controls.Add(cbCurrency);
|
||||
pnlOverviewTop.Controls.Add(lblWaehrung);
|
||||
pnlOverviewTop.Controls.Add(dtTo);
|
||||
pnlOverviewTop.Controls.Add(lblBis);
|
||||
pnlOverviewTop.Controls.Add(dtFrom);
|
||||
pnlOverviewTop.Controls.Add(lblVon);
|
||||
pnlOverviewTop.Controls.Add(cbOvAccount);
|
||||
pnlOverviewTop.Controls.Add(lblOvKonto);
|
||||
pnlOverviewTop.Dock = DockStyle.Top;
|
||||
pnlOverviewTop.Location = new Point(4, 5);
|
||||
pnlOverviewTop.Margin = new Padding(4, 5, 4, 5);
|
||||
pnlOverviewTop.Name = "pnlOverviewTop";
|
||||
pnlOverviewTop.Size = new Size(1555, 60);
|
||||
pnlOverviewTop.TabIndex = 0;
|
||||
//
|
||||
// btnExportPdf
|
||||
//
|
||||
btnExportPdf.Location = new Point(1334, 8);
|
||||
btnExportPdf.Margin = new Padding(4, 5, 4, 5);
|
||||
btnExportPdf.Name = "btnExportPdf";
|
||||
btnExportPdf.Size = new Size(129, 43);
|
||||
btnExportPdf.TabIndex = 10;
|
||||
btnExportPdf.Text = "PDF-Export";
|
||||
btnExportPdf.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnExportCsv
|
||||
//
|
||||
btnExportCsv.Location = new Point(1197, 8);
|
||||
btnExportCsv.Margin = new Padding(4, 5, 4, 5);
|
||||
btnExportCsv.Name = "btnExportCsv";
|
||||
btnExportCsv.Size = new Size(129, 43);
|
||||
btnExportCsv.TabIndex = 9;
|
||||
btnExportCsv.Text = "CSV-Export";
|
||||
btnExportCsv.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnCalc
|
||||
//
|
||||
btnCalc.Location = new Point(1046, 8);
|
||||
btnCalc.Margin = new Padding(4, 5, 4, 5);
|
||||
btnCalc.Name = "btnCalc";
|
||||
btnCalc.Size = new Size(143, 43);
|
||||
btnCalc.TabIndex = 8;
|
||||
btnCalc.Text = "Berechnen";
|
||||
btnCalc.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// cbCurrency
|
||||
//
|
||||
cbCurrency.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
cbCurrency.Items.AddRange(new object[] { "USDC", "USD", "EUR" });
|
||||
cbCurrency.Location = new Point(921, 10);
|
||||
cbCurrency.Margin = new Padding(4, 5, 4, 5);
|
||||
cbCurrency.Name = "cbCurrency";
|
||||
cbCurrency.Size = new Size(113, 33);
|
||||
cbCurrency.TabIndex = 7;
|
||||
//
|
||||
// lblWaehrung
|
||||
//
|
||||
lblWaehrung.AutoSize = true;
|
||||
lblWaehrung.Location = new Point(821, 15);
|
||||
lblWaehrung.Margin = new Padding(4, 0, 4, 0);
|
||||
lblWaehrung.Name = "lblWaehrung";
|
||||
lblWaehrung.Size = new Size(88, 25);
|
||||
lblWaehrung.TabIndex = 6;
|
||||
lblWaehrung.Text = "Währung:";
|
||||
//
|
||||
// dtTo
|
||||
//
|
||||
dtTo.Format = DateTimePickerFormat.Short;
|
||||
dtTo.Location = new Point(636, 10);
|
||||
dtTo.Margin = new Padding(4, 5, 4, 5);
|
||||
dtTo.Name = "dtTo";
|
||||
dtTo.Size = new Size(155, 31);
|
||||
dtTo.TabIndex = 5;
|
||||
//
|
||||
// lblBis
|
||||
//
|
||||
lblBis.AutoSize = true;
|
||||
lblBis.Location = new Point(593, 15);
|
||||
lblBis.Margin = new Padding(4, 0, 4, 0);
|
||||
lblBis.Name = "lblBis";
|
||||
lblBis.Size = new Size(38, 25);
|
||||
lblBis.TabIndex = 4;
|
||||
lblBis.Text = "Bis:";
|
||||
//
|
||||
// dtFrom
|
||||
//
|
||||
dtFrom.Format = DateTimePickerFormat.Short;
|
||||
dtFrom.Location = new Point(429, 10);
|
||||
dtFrom.Margin = new Padding(4, 5, 4, 5);
|
||||
dtFrom.Name = "dtFrom";
|
||||
dtFrom.Size = new Size(155, 31);
|
||||
dtFrom.TabIndex = 3;
|
||||
//
|
||||
// lblVon
|
||||
//
|
||||
lblVon.AutoSize = true;
|
||||
lblVon.Location = new Point(381, 15);
|
||||
lblVon.Margin = new Padding(4, 0, 4, 0);
|
||||
lblVon.Name = "lblVon";
|
||||
lblVon.Size = new Size(47, 25);
|
||||
lblVon.TabIndex = 2;
|
||||
lblVon.Text = "Von:";
|
||||
//
|
||||
// cbOvAccount
|
||||
//
|
||||
cbOvAccount.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
cbOvAccount.Location = new Point(79, 10);
|
||||
cbOvAccount.Margin = new Padding(4, 5, 4, 5);
|
||||
cbOvAccount.Name = "cbOvAccount";
|
||||
cbOvAccount.Size = new Size(284, 33);
|
||||
cbOvAccount.TabIndex = 1;
|
||||
//
|
||||
// lblOvKonto
|
||||
//
|
||||
lblOvKonto.AutoSize = true;
|
||||
lblOvKonto.Location = new Point(9, 15);
|
||||
lblOvKonto.Margin = new Padding(4, 0, 4, 0);
|
||||
lblOvKonto.Name = "lblOvKonto";
|
||||
lblOvKonto.Size = new Size(64, 25);
|
||||
lblOvKonto.TabIndex = 0;
|
||||
lblOvKonto.Text = "Konto:";
|
||||
//
|
||||
// tabLedger
|
||||
//
|
||||
tabLedger.Controls.Add(dgvLedger);
|
||||
tabLedger.Controls.Add(toolStripLedger);
|
||||
tabLedger.Location = new Point(4, 34);
|
||||
tabLedger.Margin = new Padding(4, 5, 4, 5);
|
||||
tabLedger.Name = "tabLedger";
|
||||
tabLedger.Padding = new Padding(4, 5, 4, 5);
|
||||
tabLedger.Size = new Size(1563, 992);
|
||||
tabLedger.TabIndex = 1;
|
||||
tabLedger.Text = "Ledger";
|
||||
tabLedger.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// dgvLedger
|
||||
//
|
||||
dgvLedger.AllowUserToAddRows = false;
|
||||
dgvLedger.AllowUserToDeleteRows = false;
|
||||
dgvLedger.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
dgvLedger.Dock = DockStyle.Fill;
|
||||
dgvLedger.Location = new Point(4, 39);
|
||||
dgvLedger.Margin = new Padding(4, 5, 4, 5);
|
||||
dgvLedger.Name = "dgvLedger";
|
||||
dgvLedger.ReadOnly = true;
|
||||
dgvLedger.RowHeadersVisible = false;
|
||||
dgvLedger.RowHeadersWidth = 62;
|
||||
dgvLedger.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
||||
dgvLedger.Size = new Size(1555, 948);
|
||||
dgvLedger.TabIndex = 1;
|
||||
//
|
||||
// toolStripLedger
|
||||
//
|
||||
toolStripLedger.ImageScalingSize = new Size(24, 24);
|
||||
toolStripLedger.Items.AddRange(new ToolStripItem[] { lblLedgerKonto, cbLedgerAccount, sepLedger, btnLedgerRefresh });
|
||||
toolStripLedger.Location = new Point(4, 5);
|
||||
toolStripLedger.Name = "toolStripLedger";
|
||||
toolStripLedger.Padding = new Padding(0, 0, 3, 0);
|
||||
toolStripLedger.Size = new Size(1555, 34);
|
||||
toolStripLedger.TabIndex = 0;
|
||||
//
|
||||
// lblLedgerKonto
|
||||
//
|
||||
lblLedgerKonto.Name = "lblLedgerKonto";
|
||||
lblLedgerKonto.Size = new Size(64, 29);
|
||||
lblLedgerKonto.Text = "Konto:";
|
||||
//
|
||||
// cbLedgerAccount
|
||||
//
|
||||
cbLedgerAccount.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
cbLedgerAccount.Name = "cbLedgerAccount";
|
||||
cbLedgerAccount.Size = new Size(341, 34);
|
||||
//
|
||||
// sepLedger
|
||||
//
|
||||
sepLedger.Name = "sepLedger";
|
||||
sepLedger.Size = new Size(6, 34);
|
||||
//
|
||||
// btnLedgerRefresh
|
||||
//
|
||||
btnLedgerRefresh.DisplayStyle = ToolStripItemDisplayStyle.Text;
|
||||
btnLedgerRefresh.Name = "btnLedgerRefresh";
|
||||
btnLedgerRefresh.Size = new Size(116, 29);
|
||||
btnLedgerRefresh.Text = "Aktualisieren";
|
||||
//
|
||||
// tabStatus
|
||||
//
|
||||
tabStatus.Controls.Add(dgvRuns);
|
||||
tabStatus.Controls.Add(toolStripStatus);
|
||||
tabStatus.Location = new Point(4, 34);
|
||||
tabStatus.Margin = new Padding(4, 5, 4, 5);
|
||||
tabStatus.Name = "tabStatus";
|
||||
tabStatus.Padding = new Padding(4, 5, 4, 5);
|
||||
tabStatus.Size = new Size(1563, 992);
|
||||
tabStatus.TabIndex = 2;
|
||||
tabStatus.Text = "Abruf / Status";
|
||||
tabStatus.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// dgvRuns
|
||||
//
|
||||
dgvRuns.AllowUserToAddRows = false;
|
||||
dgvRuns.AllowUserToDeleteRows = false;
|
||||
dgvRuns.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
dgvRuns.Dock = DockStyle.Fill;
|
||||
dgvRuns.Location = new Point(4, 39);
|
||||
dgvRuns.Margin = new Padding(4, 5, 4, 5);
|
||||
dgvRuns.Name = "dgvRuns";
|
||||
dgvRuns.ReadOnly = true;
|
||||
dgvRuns.RowHeadersVisible = false;
|
||||
dgvRuns.RowHeadersWidth = 62;
|
||||
dgvRuns.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
||||
dgvRuns.Size = new Size(1555, 948);
|
||||
dgvRuns.TabIndex = 1;
|
||||
//
|
||||
// toolStripStatus
|
||||
//
|
||||
toolStripStatus.ImageScalingSize = new Size(24, 24);
|
||||
toolStripStatus.Items.AddRange(new ToolStripItem[] { lblStatusKonto, cbStatusAccount, sepStatus, btnIncremental, btnBackfill, sepStatus2, btnStatusRefresh });
|
||||
toolStripStatus.Location = new Point(4, 5);
|
||||
toolStripStatus.Name = "toolStripStatus";
|
||||
toolStripStatus.Padding = new Padding(0, 0, 3, 0);
|
||||
toolStripStatus.Size = new Size(1555, 34);
|
||||
toolStripStatus.TabIndex = 0;
|
||||
//
|
||||
// lblStatusKonto
|
||||
//
|
||||
lblStatusKonto.Name = "lblStatusKonto";
|
||||
lblStatusKonto.Size = new Size(64, 29);
|
||||
lblStatusKonto.Text = "Konto:";
|
||||
//
|
||||
// cbStatusAccount
|
||||
//
|
||||
cbStatusAccount.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
cbStatusAccount.Name = "cbStatusAccount";
|
||||
cbStatusAccount.Size = new Size(341, 34);
|
||||
//
|
||||
// sepStatus
|
||||
//
|
||||
sepStatus.Name = "sepStatus";
|
||||
sepStatus.Size = new Size(6, 34);
|
||||
//
|
||||
// btnIncremental
|
||||
//
|
||||
btnIncremental.DisplayStyle = ToolStripItemDisplayStyle.Text;
|
||||
btnIncremental.Name = "btnIncremental";
|
||||
btnIncremental.Size = new Size(134, 29);
|
||||
btnIncremental.Text = "Inkrement jetzt";
|
||||
btnIncremental.ToolTipText = "Nur neue Ereignisse seit dem letzten Stand abrufen (mit Lookback-Überlappung).";
|
||||
//
|
||||
// btnBackfill
|
||||
//
|
||||
btnBackfill.DisplayStyle = ToolStripItemDisplayStyle.Text;
|
||||
btnBackfill.Name = "btnBackfill";
|
||||
btnBackfill.Size = new Size(188, 29);
|
||||
btnBackfill.Text = "Backfill (volle Historie)";
|
||||
btnBackfill.ToolTipText = "Komplette Historie neu abrufen (idempotent – bucht nichts doppelt).";
|
||||
//
|
||||
// sepStatus2
|
||||
//
|
||||
sepStatus2.Name = "sepStatus2";
|
||||
sepStatus2.Size = new Size(6, 34);
|
||||
//
|
||||
// btnStatusRefresh
|
||||
//
|
||||
btnStatusRefresh.DisplayStyle = ToolStripItemDisplayStyle.Text;
|
||||
btnStatusRefresh.Name = "btnStatusRefresh";
|
||||
btnStatusRefresh.Size = new Size(116, 29);
|
||||
btnStatusRefresh.Text = "Aktualisieren";
|
||||
//
|
||||
// lblAccStatus
|
||||
//
|
||||
lblAccStatus.Dock = DockStyle.Bottom;
|
||||
lblAccStatus.Location = new Point(0, 1030);
|
||||
lblAccStatus.Margin = new Padding(4, 0, 4, 0);
|
||||
lblAccStatus.Name = "lblAccStatus";
|
||||
lblAccStatus.Padding = new Padding(9, 3, 9, 3);
|
||||
lblAccStatus.Size = new Size(1571, 37);
|
||||
lblAccStatus.TabIndex = 1;
|
||||
//
|
||||
// AccountingMainForm
|
||||
//
|
||||
AutoScaleDimensions = new SizeF(10F, 25F);
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(1571, 1067);
|
||||
Controls.Add(tabControlAcc);
|
||||
Controls.Add(lblAccStatus);
|
||||
Icon = (Icon)resources.GetObject("$this.Icon");
|
||||
Margin = new Padding(4, 5, 4, 5);
|
||||
Name = "AccountingMainForm";
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
Text = "Accounting";
|
||||
tabControlAcc.ResumeLayout(false);
|
||||
tabUebersicht.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)dgvMonthly).EndInit();
|
||||
flpKpis.ResumeLayout(false);
|
||||
pnlOverviewTop.ResumeLayout(false);
|
||||
pnlOverviewTop.PerformLayout();
|
||||
tabLedger.ResumeLayout(false);
|
||||
tabLedger.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)dgvLedger).EndInit();
|
||||
toolStripLedger.ResumeLayout(false);
|
||||
toolStripLedger.PerformLayout();
|
||||
tabStatus.ResumeLayout(false);
|
||||
tabStatus.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)dgvRuns).EndInit();
|
||||
toolStripStatus.ResumeLayout(false);
|
||||
toolStripStatus.PerformLayout();
|
||||
ResumeLayout(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TabControl tabControlAcc;
|
||||
private System.Windows.Forms.TabPage tabUebersicht;
|
||||
private System.Windows.Forms.Panel pnlOverviewTop;
|
||||
private System.Windows.Forms.Label lblOvKonto;
|
||||
private System.Windows.Forms.ComboBox cbOvAccount;
|
||||
private System.Windows.Forms.Label lblVon;
|
||||
private System.Windows.Forms.DateTimePicker dtFrom;
|
||||
private System.Windows.Forms.Label lblBis;
|
||||
private System.Windows.Forms.DateTimePicker dtTo;
|
||||
private System.Windows.Forms.Label lblWaehrung;
|
||||
private System.Windows.Forms.ComboBox cbCurrency;
|
||||
private System.Windows.Forms.Button btnCalc;
|
||||
private System.Windows.Forms.Button btnExportCsv;
|
||||
private System.Windows.Forms.Button btnExportPdf;
|
||||
private System.Windows.Forms.FlowLayoutPanel flpKpis;
|
||||
private System.Windows.Forms.Label lblKpiNet;
|
||||
private System.Windows.Forms.Label lblKpiClosing;
|
||||
private System.Windows.Forms.Label lblKpiDeposits;
|
||||
private System.Windows.Forms.Label lblKpiWithdrawals;
|
||||
private System.Windows.Forms.Label lblKpiFees;
|
||||
private System.Windows.Forms.Label lblKpiRewards;
|
||||
private System.Windows.Forms.Label lblKpiVolume;
|
||||
private System.Windows.Forms.Label lblKpiTrades;
|
||||
private System.Windows.Forms.DataGridView dgvMonthly;
|
||||
private System.Windows.Forms.TabPage tabLedger;
|
||||
private System.Windows.Forms.ToolStrip toolStripLedger;
|
||||
private System.Windows.Forms.ToolStripLabel lblLedgerKonto;
|
||||
private System.Windows.Forms.ToolStripComboBox cbLedgerAccount;
|
||||
private System.Windows.Forms.ToolStripSeparator sepLedger;
|
||||
private System.Windows.Forms.ToolStripButton btnLedgerRefresh;
|
||||
private System.Windows.Forms.DataGridView dgvLedger;
|
||||
private System.Windows.Forms.TabPage tabStatus;
|
||||
private System.Windows.Forms.ToolStrip toolStripStatus;
|
||||
private System.Windows.Forms.ToolStripLabel lblStatusKonto;
|
||||
private System.Windows.Forms.ToolStripComboBox cbStatusAccount;
|
||||
private System.Windows.Forms.ToolStripSeparator sepStatus;
|
||||
private System.Windows.Forms.ToolStripButton btnIncremental;
|
||||
private System.Windows.Forms.ToolStripButton btnBackfill;
|
||||
private System.Windows.Forms.ToolStripSeparator sepStatus2;
|
||||
private System.Windows.Forms.ToolStripButton btnStatusRefresh;
|
||||
private System.Windows.Forms.DataGridView dgvRuns;
|
||||
private System.Windows.Forms.Label lblAccStatus;
|
||||
}
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PolyTrader.Modules.Accounting.Logic;
|
||||
using PolyTrader.Modules.Accounting.Persistence;
|
||||
using PolyTrader.Modules.Accounting.Services;
|
||||
using PolyTraderSharp;
|
||||
|
||||
namespace PolyTrader.Modules.Accounting.Ui
|
||||
{
|
||||
/// <summary>
|
||||
/// Hauptfenster des Accounting-Moduls: Tab „Übersicht / BWA" (neutrale Periodenabrechnung + KPIs +
|
||||
/// Monatsvergleich, Währung USDC/USD/EUR, CSV-Export), „Ledger" (Buchungssätze) und „Abruf / Status"
|
||||
/// (Ingest-Läufe, manueller Backfill/Inkrement). Layout im Designer; Aggregation pur im
|
||||
/// <see cref="AccountingEngine"/>. Die US-Steuerschicht (A-3) ist bewusst NICHT hier – sie hängt an
|
||||
/// den CPA-Antworten.
|
||||
/// </summary>
|
||||
public partial class AccountingMainForm : Form
|
||||
{
|
||||
private ILedgerRepository? _ledger;
|
||||
private IIngestRunRepository? _runs;
|
||||
private AccountingIngestService? _ingest;
|
||||
private AccountingReportService? _report;
|
||||
private TradingState? _state;
|
||||
|
||||
public AccountingMainForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Übersicht / BWA
|
||||
btnCalc.Click += (_, _) => Recalculate();
|
||||
btnExportCsv.Click += (_, _) => ExportCsv();
|
||||
btnExportPdf.Click += (_, _) => ExportPdf();
|
||||
cbOvAccount.SelectedIndexChanged += (_, _) => Recalculate();
|
||||
cbCurrency.SelectedIndexChanged += (_, _) => Recalculate();
|
||||
|
||||
// Ledger + Status
|
||||
btnLedgerRefresh.Click += (_, _) => LoadLedger();
|
||||
cbLedgerAccount.SelectedIndexChanged += (_, _) => LoadLedger();
|
||||
btnStatusRefresh.Click += (_, _) => LoadRuns();
|
||||
btnIncremental.Click += async (_, _) => await RunIngestAsync(backfill: false);
|
||||
btnBackfill.Click += async (_, _) => await RunIngestAsync(backfill: true);
|
||||
}
|
||||
|
||||
public void Initialize(IServiceProvider services)
|
||||
{
|
||||
_ledger = services.GetRequiredService<ILedgerRepository>();
|
||||
_runs = services.GetRequiredService<IIngestRunRepository>();
|
||||
_ingest = services.GetRequiredService<AccountingIngestService>();
|
||||
_report = services.GetRequiredService<AccountingReportService>();
|
||||
_state = services.GetRequiredService<TradingState>();
|
||||
|
||||
// Standard-Zeitraum: laufender Monat.
|
||||
var now = DateTime.Now;
|
||||
dtFrom.Value = new DateTime(now.Year, now.Month, 1);
|
||||
dtTo.Value = now;
|
||||
if (cbCurrency.Items.Count > 0) cbCurrency.SelectedIndex = 0; // USDC
|
||||
|
||||
PopulateAccounts();
|
||||
Recalculate();
|
||||
LoadLedger();
|
||||
LoadRuns();
|
||||
}
|
||||
|
||||
// ---------------- Konten-Filter ----------------
|
||||
|
||||
private void PopulateAccounts()
|
||||
{
|
||||
var items = new List<AccountItem> { new(null, "Alle Live-Konten") };
|
||||
if (_state != null)
|
||||
items.AddRange(_state.Accounts.Values
|
||||
.Where(a => !a.IsDemo && !string.IsNullOrWhiteSpace(a.WalletAddress))
|
||||
.OrderBy(a => a.AccountId)
|
||||
.Select(a => new AccountItem(a.AccountId, string.IsNullOrEmpty(a.Name) ? $"#{a.AccountId}" : $"{a.Name} (#{a.AccountId})")));
|
||||
|
||||
cbOvAccount.DisplayMember = nameof(AccountItem.Label);
|
||||
cbOvAccount.DataSource = new List<AccountItem>(items);
|
||||
foreach (var combo in new[] { cbLedgerAccount, cbStatusAccount })
|
||||
{
|
||||
combo.ComboBox.DisplayMember = nameof(AccountItem.Label);
|
||||
combo.ComboBox.DataSource = new List<AccountItem>(items);
|
||||
}
|
||||
}
|
||||
|
||||
private static int? IdOf(object? item) => item is AccountItem it ? it.Id : null;
|
||||
|
||||
// ---------------- Übersicht / BWA ----------------
|
||||
|
||||
private void Recalculate()
|
||||
{
|
||||
if (_report == null) return;
|
||||
try
|
||||
{
|
||||
DateTime from = dtFrom.Value.Date;
|
||||
DateTime to = dtTo.Value.Date.AddDays(1).AddTicks(-1); // inklusive gewählter Bis-Tag
|
||||
int? accId = IdOf(cbOvAccount.SelectedItem);
|
||||
|
||||
var statement = _report.BuildStatement(accId, from, to);
|
||||
var monthly = _report.BuildMonthly(accId, from, to);
|
||||
var cur = _report.ResolveCurrency(cbCurrency.SelectedItem?.ToString() ?? "USDC", to);
|
||||
|
||||
UpdateKpis(statement, cur);
|
||||
dgvMonthly.DataSource = monthly.Select(m => new MonthlyRow(m, cur)).ToList();
|
||||
|
||||
lblAccStatus.Text = cur.Available
|
||||
? $"Währung {cur.Code}: {cur.Note}"
|
||||
: $"⚠️ {cur.Note} (Anzeige in USDC).";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lblAccStatus.Text = $"Abrechnung nicht möglich (acc_-Migration angewendet?): {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateKpis(PeriodStatement s, CurrencyContext cur)
|
||||
{
|
||||
CurrencyContext view = cur.Available ? cur : new CurrencyContext("USDC", 1m, true, "");
|
||||
string unit = view.Code;
|
||||
decimal V(decimal usdc) => AccountingReportService.Convert(usdc, view);
|
||||
|
||||
lblKpiNet.Text = $"Netto-Handelsergebnis\n{V(s.NetTradingResultUsdc):N2} {unit}";
|
||||
lblKpiNet.ForeColor = s.NetTradingResultUsdc >= 0 ? System.Drawing.Color.ForestGreen : System.Drawing.Color.Firebrick;
|
||||
lblKpiClosing.Text = $"Endsaldo\n{V(s.ClosingBalanceUsdc):N2} {unit}";
|
||||
lblKpiDeposits.Text = $"Einzahlungen\n{V(s.Deposits):N2} {unit}";
|
||||
lblKpiWithdrawals.Text = $"Auszahlungen\n{V(s.Withdrawals):N2} {unit}";
|
||||
lblKpiFees.Text = $"Fees\n{V(s.Fees):N2} {unit}";
|
||||
lblKpiRewards.Text = $"Rewards\n{V(s.Rewards):N2} {unit}";
|
||||
lblKpiVolume.Text = $"Handelsvolumen\n{V(s.TradeVolume):N2} {unit}";
|
||||
lblKpiTrades.Text = $"Trades\n{s.TradeCount}";
|
||||
}
|
||||
|
||||
private void ExportCsv()
|
||||
{
|
||||
if (_ledger == null || _report == null) return;
|
||||
DateTime from = dtFrom.Value.Date;
|
||||
DateTime to = dtTo.Value.Date.AddDays(1).AddTicks(-1);
|
||||
int? accId = IdOf(cbOvAccount.SelectedItem);
|
||||
|
||||
using var dlg = new SaveFileDialog
|
||||
{
|
||||
Filter = "CSV-Datei (*.csv)|*.csv",
|
||||
FileName = $"accounting_{(accId?.ToString() ?? "alle")}_{from:yyyyMMdd}-{dtTo.Value:yyyyMMdd}.csv"
|
||||
};
|
||||
if (dlg.ShowDialog(this) != DialogResult.OK) return;
|
||||
|
||||
try
|
||||
{
|
||||
var statement = _report.BuildStatement(accId, from, to);
|
||||
var entries = _ledger.Query(accId, from, to, 100000);
|
||||
string csv = CsvExporter.Statement(statement) + Environment.NewLine + CsvExporter.Ledger(entries);
|
||||
System.IO.File.WriteAllText(dlg.FileName, csv, new System.Text.UTF8Encoding(true));
|
||||
lblAccStatus.Text = $"CSV exportiert: {dlg.FileName} ({entries.Count} Buchungen).";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lblAccStatus.Text = $"CSV-Export fehlgeschlagen: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportPdf()
|
||||
{
|
||||
if (_ledger == null || _report == null) return;
|
||||
DateTime from = dtFrom.Value.Date;
|
||||
DateTime to = dtTo.Value.Date.AddDays(1).AddTicks(-1);
|
||||
int? accId = IdOf(cbOvAccount.SelectedItem);
|
||||
|
||||
using var dlg = new SaveFileDialog
|
||||
{
|
||||
Filter = "PDF-Datei (*.pdf)|*.pdf",
|
||||
FileName = $"abrechnung_{(accId?.ToString() ?? "alle")}_{from:yyyyMMdd}-{dtTo.Value:yyyyMMdd}.pdf"
|
||||
};
|
||||
if (dlg.ShowDialog(this) != DialogResult.OK) return;
|
||||
|
||||
try
|
||||
{
|
||||
var statement = _report.BuildStatement(accId, from, to);
|
||||
var monthly = _report.BuildMonthly(accId, from, to);
|
||||
var entries = _ledger.Query(accId, from, to, 100000);
|
||||
var cur = _report.ResolveCurrency(cbCurrency.SelectedItem?.ToString() ?? "USDC", to);
|
||||
CurrencyContext view = cur.Available ? cur : new CurrencyContext("USDC", 1m, true, "Native Buchungswährung.");
|
||||
|
||||
byte[] pdf = PdfExporter.Render(statement, monthly, entries, view.Code, view.Factor, view.Note);
|
||||
System.IO.File.WriteAllBytes(dlg.FileName, pdf);
|
||||
lblAccStatus.Text = $"PDF exportiert: {dlg.FileName} ({entries.Count} Buchungen, {view.Code}).";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lblAccStatus.Text = $"PDF-Export fehlgeschlagen: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- Ledger ----------------
|
||||
|
||||
private void LoadLedger()
|
||||
{
|
||||
if (_ledger == null) return;
|
||||
try
|
||||
{
|
||||
var rows = _ledger.Query(IdOf(cbLedgerAccount.SelectedItem), null, null, 1000);
|
||||
dgvLedger.DataSource = rows.Select(e => new LedgerRow(e)).ToList();
|
||||
lblAccStatus.Text = rows.Count == 0
|
||||
? "Noch keine Buchungen. (Ingest-Quellen sind offline bis zur Live-Anbindung im Zielland.)"
|
||||
: $"{rows.Count} Buchungssätze angezeigt.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lblAccStatus.Text = $"Ledger nicht lesbar (acc_-Migration angewendet?): {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- Abruf / Status ----------------
|
||||
|
||||
private void LoadRuns()
|
||||
{
|
||||
if (_runs == null) return;
|
||||
try
|
||||
{
|
||||
dgvRuns.DataSource = _runs.GetRecent(IdOf(cbStatusAccount.SelectedItem), 100);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lblAccStatus.Text = $"Ingest-Läufe nicht lesbar: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task RunIngestAsync(bool backfill)
|
||||
{
|
||||
if (_ingest == null) return;
|
||||
btnBackfill.Enabled = false;
|
||||
btnIncremental.Enabled = false;
|
||||
lblAccStatus.Text = backfill ? "Backfill läuft …" : "Inkrementeller Abruf läuft …";
|
||||
try
|
||||
{
|
||||
await System.Threading.Tasks.Task.Run(() => _ingest.IngestAllAsync(backfill, CancellationToken.None));
|
||||
lblAccStatus.Text = "Abruf abgeschlossen.";
|
||||
LoadRuns();
|
||||
LoadLedger();
|
||||
Recalculate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lblAccStatus.Text = $"Abruf-Fehler: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
btnBackfill.Enabled = true;
|
||||
btnIncremental.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record AccountItem(int? Id, string Label);
|
||||
|
||||
/// <summary>Anzeige-Zeile des Monatsvergleichs (Beträge in der gewählten Währung).</summary>
|
||||
private sealed class MonthlyRow
|
||||
{
|
||||
public MonthlyRow(PeriodStatement m, CurrencyContext cur)
|
||||
{
|
||||
CurrencyContext view = cur.Available ? cur : new CurrencyContext("USDC", 1m, true, "");
|
||||
decimal V(decimal usdc) => AccountingReportService.Convert(usdc, view);
|
||||
Monat = m.From.ToString("yyyy-MM");
|
||||
Anfangssaldo = V(m.OpeningBalanceUsdc);
|
||||
Einzahlungen = V(m.Deposits);
|
||||
Auszahlungen = V(m.Withdrawals);
|
||||
Handelsvolumen = V(m.TradeVolume);
|
||||
Rewards = V(m.Rewards);
|
||||
Fees = V(m.Fees);
|
||||
Handelsergebnis = V(m.NetTradingResultUsdc);
|
||||
Endsaldo = V(m.ClosingBalanceUsdc);
|
||||
Trades = m.TradeCount;
|
||||
}
|
||||
|
||||
public string Monat { get; }
|
||||
public decimal Anfangssaldo { get; }
|
||||
public decimal Einzahlungen { get; }
|
||||
public decimal Auszahlungen { get; }
|
||||
public decimal Handelsvolumen { get; }
|
||||
public decimal Rewards { get; }
|
||||
public decimal Fees { get; }
|
||||
public decimal Handelsergebnis { get; }
|
||||
public decimal Endsaldo { get; }
|
||||
public int Trades { get; }
|
||||
}
|
||||
|
||||
/// <summary>Anzeige-Zeile fürs Ledger-Grid.</summary>
|
||||
private sealed class LedgerRow
|
||||
{
|
||||
public LedgerRow(Models.LedgerEntry e)
|
||||
{
|
||||
Zeit = e.Timestamp;
|
||||
Konto = e.AccountId;
|
||||
Typ = e.EventType.ToString();
|
||||
Markt = e.MarketSlug;
|
||||
Outcome = e.Outcome;
|
||||
Side = e.Side;
|
||||
Size = e.Size;
|
||||
Preis = e.PriceUsdc;
|
||||
Brutto = e.GrossUsdc;
|
||||
Fee = e.FeeUsdc;
|
||||
Netto = e.NetUsdc;
|
||||
TxHash = e.TxHash;
|
||||
Quelle = e.Source;
|
||||
}
|
||||
|
||||
public DateTime Zeit { get; }
|
||||
public int Konto { get; }
|
||||
public string Typ { get; }
|
||||
public string Markt { get; }
|
||||
public string Outcome { get; }
|
||||
public string Side { get; }
|
||||
public decimal Size { get; }
|
||||
public decimal Preis { get; }
|
||||
public decimal Brutto { get; }
|
||||
public decimal Fee { get; }
|
||||
public decimal Netto { get; }
|
||||
public string TxHash { get; }
|
||||
public string Quelle { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="toolStripLedger.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="toolStripStatus.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>206, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
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
|
||||
AABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
Reference in New Issue
Block a user