Rename solution directory to Elwig

This commit is contained in:
2023-03-18 11:04:22 +01:00
parent 649578e8bf
commit 769d80eb81
48 changed files with 7 additions and 11 deletions

33
Elwig/App.xaml Normal file
View File

@ -0,0 +1,33 @@
<Application x:Class="Elwig.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Elwig"
StartupUri="Windows\MainWindow.xaml"
xmlns:ui="http://schemas.modernwpf.com/2019">
<Application.Resources>
<!--<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources />
<ui:XamlControlsResources />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>-->
<DataTemplate x:Key="PostalDestComboBoxTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Dest}"/>
<TextBlock Text=" ("/>
<TextBlock Text="{Binding Ort.Name}"/>
<TextBlock Text=")"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="BranchTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="KgTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</Application.Resources>
</Application>

47
Elwig/App.xaml.cs Normal file
View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.IO;
using Elwig.Helpers;
namespace Elwig {
public partial class App : Application {
public static bool IsPrintingReady => Documents.Html.IsReady && Documents.Pdf.IsReady;
public static System.Windows.Threading.Dispatcher MainDispatcher { get; private set; }
public App() : base() {
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "Elwig"));
MainDispatcher = Dispatcher;
}
protected override void OnStartup(StartupEventArgs evt) {
Utils.RunBackground("HTML Initialization", () => Documents.Html.Init(PrintingReadyChanged));
Utils.RunBackground("PDF Initialization", () => Documents.Pdf.Init(PrintingReadyChanged));
base.OnStartup(evt);
}
protected override void OnExit(ExitEventArgs evt) {
Utils.RunBackground("PDF Close", () => Documents.Pdf.Close());
base.OnExit(evt);
}
private void PrintingReadyChanged() {
Dispatcher.BeginInvoke(OnPrintingReadyChanged, new EventArgs());
}
protected void OnPrintingReadyChanged(EventArgs evt) {
foreach (Window w in Windows) {
foreach (var b in Utils.FindVisualChilds<Button>(w).Where(b => "Print".Equals(b.Tag))) {
b.IsEnabled = IsPrintingReady;
}
}
}
}
}

10
Elwig/AssemblyInfo.cs Normal file
View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@ -0,0 +1,18 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.BusinessDocument>
@model Elwig.Documents.BusinessDocument
@{ Layout = "Document"; }
<div class="info-wrapper">
<div class="address-wrapper">
<div class="sender">
<div>WG Matzen | Schloßstraße 6 | 2243 Matzen</div>
<div>E Österreichische Post AG Eco Brief</div>
</div>
<address>@Model.Address</address>
</div>
<aside></aside>
</div>
<main>
@RenderBody()
</main>

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Elwig.Models;
namespace Elwig.Documents {
public abstract class BusinessDocument : Document {
public BusinessDocument(string title, Member m) : base(title) {
Member = m;
}
public Member Member { get; set; }
public string Address {
get {
// TODO Name/Rechnungsadresse
return $"{Member.GivenName} {Member.FamilyName}\n{Member.Address}";
}
}
}
}

View File

@ -0,0 +1,8 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.BusinessLetter>
@model Elwig.Documents.BusinessLetter
@{ Layout = "BusinessDocument"; }
<p>Sehr geehrtes Mitglied,</p>
<p>nein.</p>
<p>Mit freundlichen Grüßen<br/>Ihre Winzergenossenschaft</p>

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Elwig.Models;
namespace Elwig.Documents {
public class BusinessLetter : BusinessDocument {
public BusinessLetter(string title, Member m) : base(title, m) {
}
}
}

View File

@ -0,0 +1,38 @@
@using RazorLight
@inherits TemplatePage<WGneu.Documents.Document>
@model WGneu.Documents.Document
<!DOCTYPE html>
<html lang="de-AT">
<head>
<title>@Model.Title</title>
<meta charset="UTF-8"/>
<script>
window.PagedConfig = { auto: false };
if (!navigator.webdriver) {
window.addEventListener("beforeprint", async () => { await window.PagedPolyfill.preview(); });
window.addEventListener("afterprint", () => { location.reload(); });
}
</script>
<!-- TODO store paged.js locally to avoid using the internet -->
<script src="https://unpkg.com/pagedjs/dist/paged.polyfill.js"></script>
@{ await IncludeAsync("Style.css"); }
</head>
<body>
<div class="m1"></div>
<div class="m2"></div>
<div class="m3"></div>
<header>@Raw(Model.Header)</header>
<div class="footer-wrapper">
<div class="pre-footer">
<span class="date">@Model.FullDateString</span>
<span class="page"></span>
</div>
<footer>@Raw(Model.Footer)</footer>
</div>
<div class="spacing"></div>
<div class="main-wrapper">
@RenderBody()
</div>
</body>
</html>

View File

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using Elwig.Helpers;
namespace Elwig.Documents {
public abstract class Document : IDisposable {
private TempFile? PdfFile = null;
public Document(string title) {
Title = title;
Header = "<h1>Winzergenossenschaft Matzen</h1>";
Footer = "Winzergenossenschaft für Matzen und Umgebung reg. Gen.m.b.H.";
Date = DateTime.Today;
}
~Document() {
Dispose();
}
public void Dispose() {
PdfFile?.Dispose();
PdfFile = null;
}
public string Title { get; set; }
public string Header { get; set; }
public string Footer { get; set; }
public string FullDateString {
get => Date.ToString("dddd, d. MMMM yyyy");
}
public DateTime Date { get; set; }
private async Task<string> Render() {
string name;
if (this is BusinessLetter) {
name = "BusinessLetter";
} else {
throw new InvalidOperationException();
}
return await Html.CompileRenderAsync(name, this);
}
public async Task Generate() {
var pdf = new TempFile("pdf");
using (var tmpHtml = new TempFile("html")) {
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render());
await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath);
}
Pdf.UpdateMetadata(pdf.FilePath, Title, "Winzergenossenschaft für Matzen und Umgebung reg. Gen.m.b.H.");
PdfFile = pdf;
}
public void SaveTo(string pdfPath) {
if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
File.Copy(PdfFile.FilePath, pdfPath);
}
public async Task Print() {
if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
Pdf.Print(PdfFile.FilePath);
}
public void Show() {
if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
Pdf.Show(PdfFile.NewReference(), Title);
}
}
}

31
Elwig/Documents/Html.cs Normal file
View File

@ -0,0 +1,31 @@
using RazorLight;
using RazorLight.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Elwig.Documents {
public static class Html {
private static RazorLightEngine? Engine = null;
public static bool IsReady => Engine != null;
public static async Task Init(Action evtHandler) {
var e = new RazorLightEngineBuilder()
.UseFileSystemProject(@"C:\Users\Lorenz\source\repos\WGneu\Elwig\Documents")
.UseMemoryCachingProvider()
.Build();
await e.CompileTemplateAsync("BusinessLetter");
Engine = e;
evtHandler();
}
public static async Task<string> CompileRenderAsync(string key, object model) {
if (Engine == null) throw new InvalidOperationException("The razor engine has not been initialized yet");
return await Engine.CompileRenderAsync(key, model);
}
}
}

87
Elwig/Documents/Pdf.cs Normal file
View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Shapes;
using PdfSharp.Pdf.IO;
using PuppeteerSharp;
using PuppeteerSharp.Media;
using RazorLight;
using Elwig.Helpers;
using Elwig.Windows;
namespace Elwig.Documents {
public static class Pdf {
private static readonly string CHROMIUM = @"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe";
private static IBrowser? Browser = null;
public static bool IsReady => Browser != null;
public static async Task Init(Action evtHandler) {
Browser = await Puppeteer.LaunchAsync(new LaunchOptions {
Headless = true,
ExecutablePath = CHROMIUM,
});
evtHandler();
}
public static async Task Close() {
if (Browser == null) return;
await Browser.CloseAsync();
Browser = null;
}
public static async Task Convert(string htmlPath, string pdfPath) {
if (Browser == null) throw new InvalidOperationException("The puppeteer engine has not been initialized yet");
using var page = await Browser.NewPageAsync();
page.Console += OnConsole;
await page.GoToAsync($"file://{htmlPath}");
await page.EvaluateFunctionAsync("async () => { await window.PagedPolyfill.preview(); }");
await page.PdfAsync(pdfPath, new() {
PreferCSSPageSize = true,
//Format = PaperFormat.A4,
DisplayHeaderFooter = false,
MarginOptions = new() {
Top = "0mm",
Right = "0mm",
Bottom = "0mm",
Left = "0mm",
},
});
}
private static void OnConsole(object sender, ConsoleEventArgs e) {
MessageBox.Show(e.Message.Text);
}
public static void UpdateMetadata(string path, string title, string author) {
using var doc = PdfReader.Open(path);
doc.Info.Title = title;
doc.Info.Author = author;
doc.Save(path);
}
public static void Show(TempFile file, string title) {
App.MainDispatcher.BeginInvoke(() => {
var w = new DocumentViewerWindow(title, file);
w.Show();
});
}
public static void Show(string path, string title) {
App.MainDispatcher.BeginInvoke(() => {
var w = new DocumentViewerWindow(title, path);
w.Show();
});
}
public static void Print(string path) {
// TODO print pdf
}
}
}

View File

@ -0,0 +1,180 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.Document>
<style>
:root {
font-family: "Times New Roman", serif;
line-height: 1;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.m1, .m2, .m3 {
height: 0;
width: 1cm;
position: fixed;
left: 0;
border-top: 1pt solid black;
}
.m1 {top: 105mm;}
.m2 {top: 148.5mm;}
.m3 {top: 210mm;}
header {
height: 45mm;
padding: 5mm;
position: absolute;
top: -25mm;
left: 0;
right: 0;
text-align: center;
}
.spacing {height: 20mm;}
.info-wrapper {
width: 100%;
height:45mm;
margin: 0 0 8.46mm 0;
position: relative;
}
.address-wrapper {
height: 45mm;
width: 85mm;
margin: 0;
padding: 5mm;
position: absolute;
left: 20mm;
top: 0;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.address-wrapper .sender {
flex: 17.7mm 1 1;
font-size: 8pt;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding-bottom: 2mm;
}
address {
flex: 27.3mm 1 1;
white-space: pre-line;
font-size: 12pt;
font-style: normal;
}
aside {
height: 40mm;
width: 75mm;
margin: 0;
position: absolute;
left: 125mm;
top: 5mm;
}
main {
margin: 8.46mm 20mm 4.23mm 25mm;
}
main :first-child {
margin-top: 0;
}
main h1, main p {
font-size: 12pt;
margin: 1em 0;
text-align: justify;
widows: 3;
orphans: 3;
}
main .date {
margin-bottom: 2em;
text-align: right;
}
main h1 {
margin-bottom: 2em;
}
.footer-wrapper {
padding: 0 20mm 0 25mm;
position: running(page-footer);
bottom: 0;
left: 0;
right: 0;
}
.pre-footer {
margin: 4.23mm 0;
font-size: 10pt;
display: flex;
}
.pre-footer .date, .pre-footer .page {
flex: 100px 1 1;
}
.pre-footer .date {text-align: left;}
.pre-footer .page {text-align: right;}
.page::after {
content: "Seite 1 von 1";
}
footer {
font-size: 10pt;
border-top: 1pt solid black;
height: 25mm;
padding-top: 1mm;
text-align: center;
}
@@page {
size: A4;
margin: 25mm 0 35mm 0;
@@bottom-center {
content: element(page-footer);
}
}
@@media screen {
body, header, .footer-wrapper {
width: 210mm;
}
header, .address-wrapper, aside, main {
border: 1px solid lightgray;
}
.m1, .m2, .m3 {display: none;}
header {top: 0;}
.spacing {height: 45mm;}
.main-wrapper {
margin-bottom: 40mm;
}
.footer-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
}
}
@@media print {
.page::after {
content: "Seite " counter(page) " von " counter(pages);
}
.footer-wrapper {
display: none;
}
}
</style>

30
Elwig/Elwig.csproj Normal file
View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>elwig.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Content Include="elwig.ico" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Razor" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.3" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1587.40" />
<PackageReference Include="ModernWpfUI" Version="0.9.6" />
<PackageReference Include="PdfSharp" Version="1.50.5147" />
<PackageReference Include="PuppeteerSharp" Version="9.0.2" />
<PackageReference Include="RazorLight" Version="2.3.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore;
using Elwig.Models;
namespace Elwig.Helpers {
public class AppDbContext : DbContext {
public DbSet<Country> Countries { get; set; }
public DbSet<Member> Members { get; set; }
public DbSet<AT_Gem> Gemeinden { get; set; }
public DbSet<AT_Kg> Katastralgemeinden { get; set; }
public DbSet<AT_Ort> Orte { get; set; }
public DbSet<AT_PlzDest> Postleitzahlen { get; set; }
public DbSet<PostalDest> PostalDestinations { get; set; }
public DbSet<Branch> Branches { get; set; }
public DbSet<WbKg> WbKgs { get; set; }
public DbSet<WbRd> WbRde { get; set; }
public DbSet<AreaCommitment> AreaCommitments { get; set; }
public DbSet<Contract> Contracts { get; set; }
public DbSet<WineAttr> WineAttributes { get; set; }
public DbSet<WineCult> WineCultivations { get; set; }
public DbSet<WineQual> WineQualities { get; set; }
public DbSet<WineVar> WineVarieties { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder.UseSqlite("Data Source=\"C:\\Users\\lorenz\\Desktop\\wgprod.sqlite3\"; foreign keys=true");
optionsBuilder.UseLazyLoadingProxies();
}
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Elwig.Helpers {
// 1 °KMW =
// 1 °NM = kg/100L = 10g/L
// 1 °Oe =
// 1 °Bé =
// 1 °Bx = g/100g (x Gramm Zucker pro 100 Gramm Flüssigkeit)
internal class Gradation {
/// <summary>
/// Gradation in mg/L.
/// </summary>
uint mgpl;
public Gradation(uint mgpl) {
this.mgpl = mgpl;
}
public static double relativeDensity(double todo) {
return 0;
}
public static double KmwToOe(double kmw) {
return 0; // TODO
}
public static double OeToKmw(double oe) {
return 0; // TODO
}
public static Gradation FromKmw(double kwm) {
return new Gradation(0); // TODO
}
public static Gradation FromKmw(double kmw, double t) {
// The temperature can be ignored, because no volumetric unit is involved.
// 1 °KMW = 1g/100g
return FromKmw(kmw);
}
public static Gradation FromOe(double oe) {
return new Gradation(0); // TODO
}
public static Gradation FromOe(double oe, double t) {
return null;
}
}
}

45
Elwig/Helpers/TempFile.cs Normal file
View File

@ -0,0 +1,45 @@
using System;
using System.IO;
namespace Elwig.Helpers {
public sealed class TempFile : IDisposable {
private int Usages = 0;
public string FilePath { get; private set; }
public TempFile() : this(null) { }
public TempFile(string? ext) : this(Path.Combine(Path.GetTempPath(), "Elwig"), ext) { }
public TempFile(string dir, string? ext) {
FilePath = Path.Combine(dir, Path.GetRandomFileName().Replace(".", "") + (ext != null ? $".{ext}" : ""));
Usages++;
Create();
}
~TempFile() {
Delete();
}
public void Dispose() {
if (--Usages == 0) {
Delete();
GC.SuppressFinalize(this);
}
}
public TempFile NewReference() {
Usages++;
return this;
}
private void Create() {
using (File.Create(FilePath)) { };
}
private void Delete() {
if (FilePath == null) return;
File.Delete(FilePath);
FilePath = null;
}
}
}

99
Elwig/Helpers/Utils.cs Normal file
View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows;
using System.Windows.Controls;
using System.Diagnostics;
using System.Windows.Controls.Primitives;
namespace Elwig.Helpers {
public static class Utils {
public static void SetInputChanged(Control input) {
var brush = Brushes.Orange;
if (input is ComboBox cb) {
var border = GetComboBoxBorder(cb);
if (border != null)
border.BorderBrush = brush;
} else {
input.BorderBrush = brush;
}
}
public static void SetInputInvalid(Control input) {
var brush = Brushes.Red;
if (input is ComboBox cb) {
var border = GetComboBoxBorder(cb);
if (border != null)
border.BorderBrush = brush;
} else {
input.BorderBrush = brush;
}
}
public static void ClearInputState(Control input) {
if (input is ComboBox cb) {
GetComboBoxBorder(cb)?.ClearValue(Border.BorderBrushProperty);
} else {
input.ClearValue(Control.BorderBrushProperty);
}
}
private static Border? GetComboBoxBorder(ComboBox cb) {
var toggleButton = cb.Template.FindName("toggleButton", cb) as ToggleButton;
return toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
}
public static IEnumerable<T> FindVisualChilds<T>(DependencyObject depObj) where T : DependencyObject {
if (depObj == null)
yield return (T)Enumerable.Empty<T>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) {
DependencyObject ithChild = VisualTreeHelper.GetChild(depObj, i);
if (ithChild == null)
continue;
if (ithChild is T t)
yield return t;
foreach (T childOfChild in FindVisualChilds<T>(ithChild))
yield return childOfChild;
}
}
public static IEnumerable<T> FindVisualChilds<T>(DependencyObject depObj, IEnumerable<DependencyObject> exempt) where T : DependencyObject {
return FindVisualChilds<T>(depObj).Where(c => !exempt.Contains(c));
}
public static int Modulo(string a, int b) {
if (!a.All(char.IsDigit))
throw new ArgumentException("First argument has to be a decimal string");
return a.Select(ch => ch - '0').Aggregate((sum, n) => (sum * 10 + n) % b);
}
public static void RunBackground(string title, Func<Task> a) {
Task.Run(async () => {
try {
await a();
} catch (Exception e) {
MessageBox.Show(e.ToString(), title, MessageBoxButton.OK, MessageBoxImage.Error);
}
});
}
public static void MailTo(string emailAddress) {
Process.Start(new ProcessStartInfo() {
FileName = $"mailto:{emailAddress}",
UseShellExecute = true,
});
}
public static bool MgNrExists(AppDbContext ctx, int mgnr) {
return ctx.Members.Find(mgnr) != null;
}
public static int NextMgNr(AppDbContext ctx) {
int c = ctx.Members.Select(m => m.MgNr).Min();
ctx.Members.OrderBy(m => m.MgNr).Select(m => m.MgNr).ToList().ForEach(a => { if (a <= c + 100) c = a; });
return c + 1;
}
}
}

300
Elwig/Helpers/Validator.cs Normal file
View File

@ -0,0 +1,300 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using Elwig.Models;
namespace Elwig.Helpers {
static class Validator {
private static readonly Dictionary<string, string[][]> PHONE_NRS = new() {
{ "43", new string[][] {
Array.Empty<string>(),
new string[] { "57", "59" },
new string[] {
"50", "517", "718", "804", "720", "780", "800", "802", "810",
"820", "821", "828", "900", "901", "930", "931", "939",
"650", "651", "652", "653", "655", "657", "659", "660", "661",
"663", "664", "665", "666", "667", "668", "669", "67", "68", "69"
}
} },
{ "49", Array.Empty<string[]>() },
{ "48", Array.Empty<string[]>() },
{ "420", Array.Empty<string[]>() },
{ "421", Array.Empty<string[]>() },
{ "36", Array.Empty<string[]>() },
{ "386", Array.Empty<string[]>() },
{ "39", Array.Empty<string[]>() },
{ "33", Array.Empty<string[]>() },
{ "41", Array.Empty<string[]>() },
{ "423", Array.Empty<string[]>() },
};
public static ValidationResult CheckNumeric(TextBox input, bool optional) {
return CheckNumeric(input, optional, -1);
}
private static ValidationResult CheckNumeric(TextBox input, bool optional, int maxLen) {
string text = "";
int pos = input.CaretIndex;
for (int i = 0; i < input.Text.Length; i++) {
char ch = input.Text[i];
if (char.IsDigit(ch))
text += ch;
if (i == input.CaretIndex - 1)
pos = text.Length;
}
input.Text = text;
input.CaretIndex = pos;
if (text.Length == 0) {
if (optional) return new(true, null);
return new(false, "Wert ist nicht optional");
}
if (maxLen >= 0 && input.Text.Length > maxLen) {
input.Text = input.Text[..maxLen];
input.CaretIndex = Math.Min(pos, maxLen);
}
return new(true, null);
}
public static ValidationResult CheckPlz(TextBox input, bool optional, AppDbContext ctx) {
CheckNumeric(input, true, 4);
if (optional && input.Text.Length == 0) {
return new(true, null);
} else if (input.Text.Length != 4) {
return new(false, "PLZ zu kurz");
}
int plz = int.Parse(input.Text);
if (!ctx.Postleitzahlen.Any(p => p.Plz == plz)) {
return new(false, "Ungültige PLZ");
}
return new(true, null);
}
public static ValidationResult CheckPhoneNumber(TextBox input, bool optional) {
string text = "";
int pos = input.CaretIndex;
for (int i = 0, v = 0; i < input.Text.Length && v < 30; i++) {
char ch = input.Text[i];
if (v == 0 && input.Text.Length - i >= 2 && ch == '0' && input.Text[i + 1] == '0') {
v++; i++;
text += '+';
} else if (ch == '(' && input.Text.Length - i >= 3 && input.Text[i + 1] == '0' && input.Text[i + 2] == ')') {
i += 2;
} else if (v == 0 && ch == '0') {
v += 3;
text += "+43";
} else if (v == 0 && ch == '+') {
v++;
text += ch;
} else if (v > 0 && char.IsDigit(ch)) {
if (PHONE_NRS.Any(kv => text == "+" + kv.Key))
text += ' ';
if (text.StartsWith("+43 ")) {
var nr = text[4..];
var vws = PHONE_NRS["43"];
if (v >= 4 && v - 4 < vws.Length && vws[v - 4].Any(vw => nr.StartsWith(vw)))
text += ' ';
else if (nr == "1")
text += ' ';
else if (v == 7 && nr.Length == 4)
text += ' ';
}
v++;
text += ch;
}
if (i == input.CaretIndex - 1)
pos = text.Length;
}
input.Text = text;
input.CaretIndex = pos;
if (optional && text.Length == 0)
return new(true, null);
if (text.Length < 10)
return new(false, "Telefonnummer zu kurz");
return new(true, null);
}
public static ValidationResult CheckEmailAddress(TextBox input, bool optional) {
string text = "";
int pos = input.CaretIndex;
bool domain = false;
for (int i = 0; i < input.Text.Length && text.Length < 256; i++) {
char ch = input.Text[i];
if (domain) {
if ((char.IsAscii(ch) && char.IsLetterOrDigit(ch)) || ".-_öäüßÖÄÜẞ".Any(c => c == ch)) {
if (!(text.Last() == '.' && ch == '.'))
text += char.ToLower(ch);
}
} else {
if (ch == '@')
domain = true;
if (!char.IsControl(ch) && !char.IsWhiteSpace(ch))
text += ch;
}
if (i == input.CaretIndex - 1)
pos = text.Length;
}
input.Text = text;
input.CaretIndex = pos;
if (text.Length == 0) {
if (optional) return new(true, null);
return new(false, "E-Mail-Adresse ist nicht optional");
} else if (text[0] == '@' || !domain) {
return new(false, "E-Mail-Adresse ungültig");
}
var last = text.Split(".").Last();
if (last.Length < 2 || !last.All(ch => char.IsAscii(ch) && char.IsLower(ch)))
return new(false, "E-Mail-Adresse ungültig");
return new(true, null);
}
public static ValidationResult CheckIban(TextBox input, bool optional) {
string text = "";
int pos = input.CaretIndex;
int v = 0;
for (int i = 0; i < input.Text.Length && v < 34; i++) {
char ch = input.Text[i];
if (char.IsLetterOrDigit(ch) && char.IsAscii(ch)) {
if (((v < 2 && char.IsLetter(ch)) || (v >= 2 && v < 4 && char.IsDigit(ch)) || v >= 4) &&
((!text.StartsWith("AT") && !text.StartsWith("DE")) || char.IsDigit(ch)))
{
if (v != 0 && v % 4 == 0)
text += ' ';
v++;
text += char.ToUpper(ch);
}
}
if (i == input.CaretIndex - 1)
pos = text.Length;
if (text.StartsWith("AT") && v >= 20)
break;
else if (text.StartsWith("DE") && v >= 22)
break;
}
input.Text = text;
input.CaretIndex = pos;
if (optional && text.Length == 0) {
return new(true, null);
} else if (v < 5 || (text.StartsWith("AT") && v != 20) || (text.StartsWith("DE") && v != 22)) {
return new(false, "IBAN hat falsche Länge");
}
var validation = (text[4..] + text[..4]).Replace(" ", "")
.Select(ch => char.IsDigit(ch) ? ch.ToString() : (ch - 'A' + 10).ToString())
.Aggregate((a, b) => a + b);
if (Utils.Modulo(validation, 97) != 1)
return new(false, "Prüfsumme der IBAN ist falsch");
return new(true, null);
}
public static ValidationResult CheckBic(TextBox input, bool optional) {
string text = "";
int pos = input.CaretIndex;
for (int i = 0, v = 0; i < input.Text.Length; i++) {
char ch = input.Text[i];
if ((v < 4 || v >= 6) && char.IsAscii(ch) && char.IsLetterOrDigit(ch)) {
v++;
text += char.ToUpper(ch);
} else if (v >= 4 && v < 6 && char.IsAscii(ch) && char.IsLetter(ch)) {
v++;
text += char.ToUpper(ch);
}
if (i == input.CaretIndex - 1)
pos = text.Length;
}
if (text.Length == 0) {
if (optional) return new(true, null);
return new(false, "BIC ist nicht optional");
}
if (text.Length > 11) {
text = text[..11];
pos = Math.Min(pos, 11);
}
if (text.Length == 11 && text.EndsWith("XXX"))
text = text[..8];
input.Text = text;
input.CaretIndex = pos;
if (text.Length != 11 && text.Length != 8)
return new(false, "BIC ist ungültig");
return new(true, null);
}
public static ValidationResult CheckLfbisNr(TextBox input, bool optional) {
var res = CheckNumeric(input, true, 7);
if (!res.IsValid) {
return res;
} else if (optional && input.Text.Length == 0) {
return new(true, null);
} else if (input.Text.Length != 7) {
return new(false, "Betriebsnummer zu kurz");
}
// TODO
return new(true, "Not implemented yet");
}
public static ValidationResult CheckUstId(TextBox input, bool optional) {
// TODO
return new(true, "Not implemented yet");
}
public static ValidationResult CheckMgNr(TextBox input, bool optional, AppDbContext ctx, Member? m) {
var res = CheckNumeric(input, optional);
if (!res.IsValid) {
return res;
} else if (optional && input.Text.Length == 0) {
return new(true, null);
}
int nr = int.Parse(input.Text);
if (nr != m?.MgNr && Utils.MgNrExists(ctx, nr)) {
return new(false, "Mitgliedsnummer wird bereits verwendet");
}
return new(true, null);
}
public static ValidationResult CheckPredecessorMgNr(TextBox input, bool optional, AppDbContext ctx) {
var res = CheckNumeric(input, optional);
if (!res.IsValid) {
return res;
} else if (optional && input.Text.Length == 0) {
return new(true, null);
} else if (!Utils.MgNrExists(ctx, int.Parse(input.Text))) {
return new(false, "Ein Mitglied mit dieser Mitgliedsnummer existiert nicht");
}
return new(true, null);
}
public static ValidationResult CheckDate(TextBox input, bool optional) {
// TODO
return new(true, "Not implemented yet");
}
public static ValidationResult CheckPartialDate(TextBox input, bool optional) {
// TODO
return new(true, "Not implemented yet");
}
}
}

13
Elwig/Models/AT_Gem.cs Normal file
View File

@ -0,0 +1,13 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("AT_gem"), PrimaryKey("Gkz")]
public class AT_Gem {
[Column("gkz")]
public int Gkz { get; private set; }
[Column("name")]
public string Name { get; private set; }
}
}

19
Elwig/Models/AT_Kg.cs Normal file
View File

@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("AT_kg"), PrimaryKey("KgNr")]
public class AT_Kg {
[Column("kgnr")]
public int KgNr { get; private set; }
[Column("gkz")]
public int Gkz { get; private set; }
[Column("name")]
public string Name { get; private set; }
[ForeignKey("Gkz")]
public virtual AT_Gem Gem { get; private set; }
}
}

25
Elwig/Models/AT_Ort.cs Normal file
View File

@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("AT_ort"), PrimaryKey("Okz")]
public class AT_Ort {
[Column("okz")]
public int Okz { get; private set; }
[Column("gkz")]
public int Gkz { get; private set; }
[Column("kgnr")]
public int? KgNr { get; private set; }
[Column("name")]
public string Name { get; private set; }
[ForeignKey("Gkz")]
public virtual AT_Gem Gem { get; private set; }
[ForeignKey("KgNr")]
public virtual AT_Kg? Kg { get; private set; }
}
}

32
Elwig/Models/AT_Plz.cs Normal file
View File

@ -0,0 +1,32 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("AT_plz"), PrimaryKey("Plz")]
public class AT_Plz {
[Column("plz")]
public int Plz { get; private set; }
[Column("ort")]
public string Ort { get; private set; }
[Column("blnr")]
public int BlNr { get; private set; }
[Column("type")]
public string Type { get; private set; }
[Column("internal")]
public bool IsInternal { get; private set; }
[Column("addressable")]
public bool IsAddressable { get; private set; }
[Column("po_box")]
public bool IsPoBox { get; private set; }
[InverseProperty("AtPlz")]
public virtual ISet<AT_PlzDest> Orte { get; private set; }
}
}

View File

@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("AT_plz_dest"), PrimaryKey("Id"), Index("Plz", "Okz", IsUnique = true)]
public class AT_PlzDest {
[Column("plz")]
public int Plz { get; private set; }
[Column("okz")]
public int Okz { get; private set; }
[Column("country")]
public string CountryCode { get; private set; }
[Column("id")]
public string Id { get; private set; }
[Column("dest")]
public string Dest { get; private set; }
[ForeignKey("Plz")]
public virtual AT_Plz AtPlz { get; private set; }
[ForeignKey("Okz")]
public virtual AT_Ort Ort { get; private set; }
[ForeignKey("CountryCode")]
public virtual Country Country { get; private set; }
}
}

View File

@ -0,0 +1,46 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("area_commitment"), PrimaryKey("VNr", "KgNr", "GstNr")]
public class AreaCommitment {
[Column("vnr")]
public int VNr { get; set; }
[Column("kgnr")]
public int KgNr { get; set; }
[Column("gstnr")]
public string? GstNr { get; set; }
[Column("rdnr")]
public int? RdNr { get; set; }
[Column("area")]
public int Area { get; set; }
[Column("sortid")]
public string SortId { get; set; }
[Column("attrid")]
public string? AttrId { get; set; }
[Column("cultid")]
public string CultId { get; set; }
[ForeignKey("VNr")]
public virtual Contract Contract { get; private set; }
[ForeignKey("SortId")]
public virtual WineVar WineVar { get; private set; }
[ForeignKey("AttrId")]
public virtual WineAttr WineAttr { get; private set; }
[ForeignKey("CultId")]
public virtual WineCult WineCult { get; private set; }
[ForeignKey("KgNr, RdNr")]
public virtual WbRd? WbRd { get; private set; }
}
}

17
Elwig/Models/Branch.cs Normal file
View File

@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("branch"), PrimaryKey("ZwstId")]
public class Branch {
[Column("zwstid")]
public string ZwstId { get; set; }
[Column("name")]
public string Name { get; set; }
[InverseProperty("Branch")]
public virtual ISet<Member> Members { get; private set; }
}
}

30
Elwig/Models/Contract.cs Normal file
View File

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace Elwig.Models {
[Table("contract"), PrimaryKey("VNr")]
public class Contract {
[Column("vnr")]
public int VNr { get; set; }
[Column("mgnr")]
public int MgNr { get; set; }
[Column("year_from")]
public int YearFrom { get; set; }
[Column("year_to")]
public int? YearTo { get; set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; }
[InverseProperty("Contract")]
public virtual ISet<AreaCommitment> AreaCommitments { get; private set; }
[NotMapped]
public int Area => AreaCommitments.Select(a => a.Area).Sum();
}
}

22
Elwig/Models/Country.cs Normal file
View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("country"), PrimaryKey("Alpha2")]
public class Country {
[Column("alpha2")]
public string Alpha2 { get; private set; }
[Column("alpha3")]
public string Alpha3 { get; private set; }
[Column("num")]
public int Num { get; private set; }
[Column("name")]
public string Name { get; private set; }
[Column("is_visible")]
public bool IsVisible { get; private set; }
}
}

126
Elwig/Models/Member.cs Normal file
View File

@ -0,0 +1,126 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("member"), PrimaryKey("MgNr")]
public class Member {
[Column("mgnr")]
public int MgNr { get; set; }
[Column("predecessor_mgnr")]
public int? PredecessorMgNr { get; set; }
[Column("prefix")]
public string? Prefix { get; set; }
[Column("given_name")]
public string GivenName { get; set; }
[Column("middle_names")]
public string? MiddleName { get; set; }
[NotMapped]
public string[] MiddleNames {
get { return (MiddleName != null) ? MiddleName.Split(" ") : Array.Empty<string>(); }
set { MiddleName = (value.Length > 0) ? string.Join(" ", value) : null; }
}
[Column("family_name")]
public string FamilyName { get; set; }
[Column("suffix")]
public string? Suffix { get; set; }
[Column("birthday")]
public string? Birthday { get; set; }
[Column("entry_date")]
public string? EntryDate { get; set; }
[Column("exit_date")]
public string? ExitDate { get; set; }
[Column("business_shares")]
public int BusinessShares { get; set; }
[Column("accounting_nr")]
public string? AccountingNr { get; set; }
[Column("zwstid")]
public string? ZwstId { get; set; }
[Column("lfbis_nr")]
public string? LfbisNr { get; set; }
[Column("ustid")]
public string? UstId { get; set; }
[Column("volllieferant")]
public bool IsVollLieferant { get; set; }
[Column("buchführend")]
public bool IsBuchführend { get; set; }
[Column("funktionär")]
public bool IsFunktionär { get; set; }
[Column("active")]
public bool IsActive { get; set; }
[Column("iban")]
public string? Iban { get; set; }
[Column("bic")]
public string? Bic { get; set; }
[Column("country")]
public string CountryCode { get; set; }
[Column("postal_dest")]
public string PostalDestId { get; set; }
[Column("address")]
public string Address { get; set; }
[Column("email")]
public string? Email { get; set; }
[Column("phone_landline")]
public string? PhoneLandline { get; set; }
[Column("phone_mobile_1")]
public string? PhoneMobile1 { get; set; }
[Column("phone_mobile_2")]
public string? PhoneMobile2 { get; set; }
[Column("default_kgnr")]
public int? DefaultKgNr { get; set; }
[Column("default_contact")]
public string DefaultContact { get; set; }
[Column("comment")]
public string? Comment { get; set; }
[ForeignKey("PredecessorMgNr")]
public virtual Member? Predecessor { get; private set; }
[ForeignKey("CountryCode")]
public virtual Country Country { get; private set; }
[ForeignKey("CountryCode, PostalDestId")]
public virtual PostalDest PostalDest { get; private set; }
[ForeignKey("DefaultKgNr")]
public virtual AT_Kg? DefaultKg { get; private set; }
[ForeignKey("ZwstId")]
public virtual Branch? Branch { get; private set; }
[InverseProperty("Member")]
public virtual ISet<Contract> Contracts { get; private set; }
}
}

View File

@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("postal_dest"), PrimaryKey("CountryCode", "Id")]
public class PostalDest {
[Column("country")]
public string CountryCode { get; private set; }
[Column("id")]
public string Id { get; private set; }
[ForeignKey("CountryCode")]
public virtual Country Country { get; private set; }
[ForeignKey("Id")]
public virtual AT_PlzDest? AtPlz { get; private set; }
}
}

16
Elwig/Models/WbKg.cs Normal file
View File

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("wb_kg"), PrimaryKey("KgNr")]
public class WbKg {
[Column("kgnr")]
public int KgNr { get; set; }
[Column("glnr")]
public int? GlNr { get; set; }
[ForeignKey("KgNr")]
public virtual AT_Kg Kg { get; private set; }
}
}

19
Elwig/Models/WbRd.cs Normal file
View File

@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("wb_rd"), PrimaryKey("KgNr", "RdNr")]
public class WbRd {
[Column("kgnr")]
public int KgNr { get; set; }
[Column("rdnr")]
public int RdNr { get; set; }
[Column("name")]
public string Name { get; set; }
[ForeignKey("KgNr")]
public virtual WbKg WbKg { get; private set; }
}
}

16
Elwig/Models/WineAttr.cs Normal file
View File

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("wine_attribute"), PrimaryKey("AttrId")]
public class WineAttr {
[Column("attrid")]
public string AttrId { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("kg_per_ha")]
public int KgPerHa { get; set; }
}
}

13
Elwig/Models/WineCult.cs Normal file
View File

@ -0,0 +1,13 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("wine_cultivation"), PrimaryKey("CultId")]
public class WineCult {
[Column("cultid")]
public string CultId { get; set; }
[Column("name")]
public string Name { get; set; }
}
}

22
Elwig/Models/WineQual.cs Normal file
View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("wine_quality"), PrimaryKey("QualId")]
public class WineQual {
[Column("qualid")]
public string QualId { get; private set; }
[Column("origin_level")]
public int? OriginLevel { get; private set; }
[Column("name")]
public string Name { get; private set; }
[Column("from_kmw")]
public double? FromKmw { get; private set; }
[Column("to_kmw")]
public double? ToKmw { get; private set; }
}
}

19
Elwig/Models/WineVar.cs Normal file
View File

@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("wine_variety"), PrimaryKey("SortId")]
public class WineVar {
[Column("sortid")]
public string SortId { get; private set; }
[Column("type")]
public string Type { get; private set; }
[Column("name")]
public string Name { get; private set; }
[Column("comment")]
public string? Comment { get; private set; }
}
}

View File

@ -0,0 +1,21 @@
<Window x:Class="Elwig.Windows.ContractListWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d"
Title="Flächenbindungen" Height="450" Width="800">
<Grid>
<DataGrid x:Name="ContractList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False" SelectionChanged="ContractList_SelectionChanged">
<DataGrid.Columns>
<DataGridTextColumn Header="Vnr." Binding="{Binding Vnr}" Width="50"/>
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr}" Width="50"/>
<DataGridTextColumn Header="From" Binding="{Binding YearFrom}" Width="50"/>
<DataGridTextColumn Header="To" Binding="{Binding YearTo}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

View File

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Elwig.Helpers;
using Elwig.Models;
namespace Elwig.Windows {
public partial class ContractListWindow : Window {
private readonly AppDbContext Context = new();
public ContractListWindow(Member member) {
InitializeComponent();
}
private void RefreshContractList() {
Context.Members.Load();
List<Contract> contracts = Context.Contracts.OrderBy(c => c.MgNr).ToList();
ContractList.ItemsSource = contracts;
if (contracts.Count == 1)
ContractList.SelectedIndex = 0;
}
private void ContractList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
RefreshContractList();
}
}
}

View File

@ -0,0 +1,12 @@
<Window x:Class="Elwig.Windows.DeliveryReceptionWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d"
Title="Übernahme" Height="450" Width="800">
<Grid>
</Grid>
</Window>

View File

@ -0,0 +1,9 @@
using System.Windows;
namespace Elwig.Windows {
public partial class DeliveryReceptionWindow : Window {
public DeliveryReceptionWindow() {
InitializeComponent();
}
}
}

View File

@ -0,0 +1,15 @@
<Window x:Class="Elwig.Windows.DocumentViewerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d"
Closed="OnClosed"
Title="PDF Ansicht"
MinHeight="600" MinWidth="420" Height="750" Width="525">
<Grid>
<wv2:WebView2 Name="WebView"/>
</Grid>
</Window>

View File

@ -0,0 +1,25 @@
using System;
using System.Windows;
using Elwig.Helpers;
namespace Elwig.Windows {
public partial class DocumentViewerWindow : Window {
private TempFile? PdfFile = null;
public DocumentViewerWindow(string title, string path) {
InitializeComponent();
Title = Title + " - " + title;
WebView.Source = new($"file://{path}#view=FitH");
}
public DocumentViewerWindow(string title, TempFile file) : this(title, file.FilePath) {
PdfFile = file;
}
public void OnClosed(object sender, EventArgs evt) {
PdfFile?.Dispose();
PdfFile = null;
}
}
}

View File

@ -0,0 +1,14 @@
<Window x:Class="Elwig.Windows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d"
Title="Elwig" Height="450" Width="800" ResizeMode="CanResize" SizeToContent="Manual"
Loaded="Window_Loaded">
<Grid>
<Button x:Name="Button2" Content="Mitglieder" Margin="472,182,178,0" VerticalAlignment="Top" Click="Button2_Click"/>
<Button x:Name="Button4" Content="Generate" Margin="425,300,225,0" VerticalAlignment="Top" Click="Button4_Click" Tag="Print"/>
</Grid>
</Window>

View File

@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Intrinsics.X86;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Elwig.Documents;
using Elwig.Helpers;
namespace Elwig.Windows {
public partial class MainWindow : Window {
private readonly AppDbContext Context = new();
public MainWindow() {
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs evt) {
Context.Countries.Load();
Button4.IsEnabled = App.IsPrintingReady;
}
protected override void OnClosing(CancelEventArgs evt) {
Context.Dispose();
base.OnClosing(evt);
}
private void Button2_Click(object sender, RoutedEventArgs evt) {
var w = new MemberListWindow();
w.Show();
}
private void Button4_Click(object sender, RoutedEventArgs evt) {
Utils.RunBackground("PDF Generation", async () => {
using var letter = new BusinessLetter("Test Dokument", Context.Members.First());
await letter.Generate();
letter.Show();
});
}
}
}

View File

@ -0,0 +1,308 @@
<Window x:Class="Elwig.Windows.MemberListWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
Title="Mitglieder verwalten - Elwig" Height="600" Width="1200" MinHeight="600" MinWidth="1000"
Loaded="Window_Loaded">
<Window.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Padding" Value="2,4,2,4"/>
<Setter Property="Height" Value="25"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="Height" Value="25"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
<Style TargetType="ComboBox">
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="Height" Value="25"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
</Style>
<Style TargetType="Button">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="9,3"/>
<Setter Property="Height" Value="27"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="0.8*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1.3*"/>
<RowDefinition Height="0.9*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="370"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Mitglied">
<MenuItem x:Name="Menu_Member_SendEmail" Header="E-Mail senden" IsEnabled="False"
Click="Menu_Member_SendEmail_Click"/>
</MenuItem>
<MenuItem Header="Drucken">
<MenuItem Header="Stammdatenblatt drucken"/>
<MenuItem Header="Mitgliederliste drucken"/>
</MenuItem>
<MenuItem Header="Rundschreiben">
<MenuItem Header="Runschreiben ausschicken"/>
<MenuItem Header="Alle Stammdatenblätter ausschicken"/>
</MenuItem>
<MenuItem Header="Werkzeuge">
<MenuItem Header="Alle Mitglieder überprüfen"/>
</MenuItem>
</Menu>
<Grid Grid.RowSpan="4" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="125"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="125"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="10,7,145,0" IsReadOnly="False"
TextChanged="SearchInput_TextChanged"/>
<CheckBox x:Name="ActiveMemberInput" Content="Nur aktive anzeigen"
Checked="ActiveMemberInput_Changed" Unchecked="ActiveMemberInput_Changed"
HorizontalAlignment="Right" Margin="0,12,10,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
<DataGrid x:Name="MemberList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
SelectionChanged="MemberList_SelectionChanged"
Margin="10,39,10,47" FontSize="14" Grid.ColumnSpan="3">
<DataGrid.Columns>
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr}" Width="70"/>
<DataGridTextColumn Header="Nachname" Binding="{Binding FamilyName}" Width="4*"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="3*"/>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="NewMemberButton" Content="Neu" Click="NewMemberButton_Click"
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10,10,0,10" Width="110" Grid.Column="0"/>
<Button x:Name="EditMemberButton" Content="Bearbeiten" Click="EditMemberButton_Click" IsEnabled="False"
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,10,0,10" Width="110" Grid.Column="1"/>
<Button x:Name="DeleteMemberButton" Content="Löschen" Click="DeleteMemberButton_Click" IsEnabled="False"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,10,10,10" Width="110" Grid.Column="2"/>
<Button x:Name="SaveButton" Content="Speichern" Click="SaveButton_Click" IsEnabled="False" Visibility="Hidden"
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10,10,0,10" Width="110" Grid.Column="0"/>
<Button x:Name="ResetButton" Content="Zurücksetzen" Click="ResetButton_Click" IsEnabled="False" Visibility="Hidden"
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,10,0,10" Width="110" Grid.Column="1"/>
<Button x:Name="CancelButton" Content="Abbrechen" Click="CancelButton_Click" IsEnabled="False" Visibility="Hidden" IsCancel="True"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,10,10,10" Width="110" Grid.Column="2"/>
</Grid>
<GroupBox Header="Persönliche Daten" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Content="MgNr.:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="MgNrInput" Margin="0,10,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"
TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus"/>
<Label Content="Vorg.:" Margin="10,10,0,0" Grid.Column="2"/>
<TextBox x:Name="PredecessorMgNrInput" Margin="0,10,10,0" Width="48" Grid.Column="3" TextAlignment="Right" HorizontalAlignment="Left"
TextChanged="PredecessorMgNrInput_TextChanged" LostFocus="PredecessorMgNrInput_LostFocus"/>
<Label Content="Vorname:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="GivenNameInput" Margin="0,40,0,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/>
<Label Content="Präfix:" Margin="10,40,0,0" Grid.Column="2"/>
<TextBox x:Name="PrefixInput" Margin="0,40,10,0" Grid.Column="3"
TextChanged="TextBox_TextChanged"/>
<Label Content="Nachname:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="FamilyNameInput" Margin="0,70,0,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/>
<Label Content="Suffix:" Margin="10,70,0,0" Grid.Column="2"/>
<TextBox x:Name="SuffixInput" Margin="0,70,10,0" Grid.Column="3"
TextChanged="TextBox_TextChanged"/>
<Label Content="Geburtstag:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="BirthdayInput" Margin="0,100,0,0" Grid.Column="1" Width="78" TextAlignment="Right" HorizontalAlignment="Left"
TextChanged="BirthdayInput_TextChanged"/>
<Label Content="Adresse:" Margin="10,130,0,0"/>
<TextBox x:Name="AddressInput" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"
TextChanged="TextBox_TextChanged"/>
<Label Content="PLZ/Ort:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="PlzInput" Margin="0,160,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus"/>
<ComboBox x:Name="OrtInput" ItemTemplate="{StaticResource PostalDestComboBoxTemplate}" TextSearch.TextPath="Ort.Name"
SelectionChanged="ComboBox_SelectionChanged"
Margin="47,160,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
</Grid>
</GroupBox>
<GroupBox Header="Kontaktdaten" Grid.Column="1" Grid.Row="3" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="E-Mail-Adresse:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailInput" Margin="0,10,10,0" Grid.Column="1"
TextChanged="EmailInput_TextChanged" LostFocus="EmailInput_LostFocus"/>
<Label Content="Tel.-Nr. (Festnetz):" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="PhoneLandlineInput" Margin="0,40,10,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<Label Content="Tel.-Nr. (mobil):" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="PhoneMobile1Input" Margin="0,70,10,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<Label Content="Tel.-Nr. (mobil):" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="PhoneMobile2Input" Margin="0,100,10,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
</Grid>
</GroupBox>
<GroupBox Header="Bankverbindung" Grid.Column="1" Grid.Row="4" Margin="5,5,5,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="65"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="IBAN:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="IbanInput" Margin="0,10,10,0" Grid.Column="1"
TextChanged="IbanInput_TextChanged" LostFocus="IbanInput_LostFocus"/>
<Label Content="BIC:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="BicInput" Margin="0,40,10,0" Grid.Column="1"
TextChanged="BicInput_TextChanged" LostFocus="BicInput_LostFocus"/>
</Grid>
</GroupBox>
<GroupBox Header="Betrieb" Grid.Column="2" Grid.Row="1" Grid.RowSpan="1" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="UID:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="UstIdInput" Margin="0,10,10,0" Grid.Column="1" Width="120" HorizontalAlignment="Left"
TextChanged="UstIdInput_TextChanged" LostFocus="UstIdInput_LostFocus"/>
<Label Content="BetriebsNr.:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="LfbisNrInput" Margin="0,40,10,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
<CheckBox x:Name="BuchführendInput" Content="Buchführend" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top" IsChecked="False"/>
</Grid>
</GroupBox>
<GroupBox Header="Rechnungsadresse (optional)" Grid.Column="2" Grid.Row="2" Grid.RowSpan="1" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="65"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Name:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingName" Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="TextBox_TextChanged"/>
<Label Content="Adresse:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingAddressInput" Margin="0,40,10,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/>
<Label Content="PLZ/Ort:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingPlzInput" Margin="0,70,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="BillingPlzInput_TextChanged" LostFocus="BillingPlzInput_LostFocus"/>
<ComboBox x:Name="BillingOrtInput" ItemTemplate="{StaticResource PostalDestComboBoxTemplate}" TextSearch.TextPath="Ort.Name"
SelectionChanged="ComboBox_SelectionChanged"
Margin="47,70,10,0" Grid.Column="1"/>
</Grid>
</GroupBox>
<GroupBox Header="Genossenschaft" Grid.Column="2" Grid.Row="3" Grid.RowSpan="2" Margin="5,5,5,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Eintritt:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="EntryDateInput" Margin="0,10,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged"/>
<Label Content="Austritt:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="ExitDateInput" Margin="0,40,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged"/>
<Label Content="Geschäftsanteile:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BusinessSharesInput" Margin="0,70,10,0" Width="48" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="NumericInput_TextChanged"/>
<Label Content="BH-Konto:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="AccountingNrInput" Margin="0,100,10,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/>
<CheckBox x:Name="ActiveInput" Content="Aktiv" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top" IsChecked="False"/>
<CheckBox x:Name="VollLieferantInput" Content="Volllieferant" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top" IsChecked="False"/>
<CheckBox x:Name="FunkionärInput" Content="Funktionär" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,75,0,0" VerticalAlignment="Top" IsChecked="False"/>
<Label Content="StammZwst.:" Margin="10,130,0,0" Grid.Column="0"/>
<ComboBox x:Name="BranchInput" ItemTemplate="{StaticResource BranchTemplate}" TextSearch.TextPath="Name"
SelectionChanged="ComboBox_SelectionChanged"
Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Content="Stammgemeinde:" Margin="10,160,0,0" Grid.Column="0"/>
<ComboBox x:Name="DefaultKgInput" ItemTemplate="{StaticResource KgTemplate}" TextSearch.TextPath="Name"
SelectionChanged="ComboBox_SelectionChanged"
Margin="0,160,10,0" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/>
<TextBox x:Name="CommentInput" Margin="0,190,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="TextBox_TextChanged"/>
<Label Content="Kontaktart:" Margin="10,220,0,0" Grid.Column="0"/>
<RadioButton x:Name="ContactPostInput" GroupName="DefaultContact" Content="Post" IsEnabled="False"
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"
HorizontalAlignment="Left" Margin="0,225,0,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
<RadioButton x:Name="ContactEmailInput" GroupName="DefaultContact" Content="E-Mail" IsEnabled="False"
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"
HorizontalAlignment="Left" Margin="60,225,0,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Content="Gebundene Fläche:" Margin="10,250,0,0" Grid.Column="0"/>
<TextBlock x:Name="AreaCommitment" Text="- m²"
Grid.Column="1" HorizontalAlignment="Stretch" Margin="5,252,5,0" TextWrapping="NoWrap" VerticalAlignment="Top" FontSize="14" TextAlignment="Right"/>
<Button x:Name="ContractButton" Content="Flächenbindungen" Click="ContractButton_Click"
HorizontalAlignment="Right" Margin="10,10,10,10" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
</Grid>
</GroupBox>
</Grid>
</Window>

View File

@ -0,0 +1,674 @@
using Microsoft.EntityFrameworkCore;
using ModernWpf.Controls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Elwig.Helpers;
using Elwig.Models;
namespace Elwig.Windows {
public partial class MemberListWindow : Window {
private bool IsEditing = false;
private bool IsCreating = false;
private List<string> TextFilter = new();
private readonly Control[] ExemptInputs;
private IEnumerable<TextBox> TextBoxInputs = Array.Empty<TextBox>();
private IEnumerable<ComboBox> ComboBoxInputs = Array.Empty<ComboBox>();
private IEnumerable<CheckBox> CheckBoxInputs = Array.Empty<CheckBox>();
private IEnumerable<RadioButton> RadioButtonInputs = Array.Empty<RadioButton>();
private readonly Dictionary<Control, bool> Valid = new();
private readonly Dictionary<Control, object?> OriginalValues = new();
private readonly RoutedCommand CtrlF = new();
private readonly AppDbContext Context = new();
public MemberListWindow() {
InitializeComponent();
CtrlF.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control));
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
ExemptInputs = new Control[] {
SearchInput, ActiveMemberInput, MemberList,
NewMemberButton, EditMemberButton, DeleteMemberButton,
ResetButton, SaveButton, CancelButton
};
}
private void Window_Loaded(object sender, RoutedEventArgs evt) {
ActiveMemberInput.IsChecked = true;
BranchInput.ItemsSource = Context.Branches.OrderBy(b => b.Name).ToList();
DefaultKgInput.ItemsSource = Context.WbKgs.Select(k => k.Kg).OrderBy(k => k.Name).ToList();
TextBoxInputs = Utils.FindVisualChilds<TextBox>(this, ExemptInputs).ToList();
ComboBoxInputs = Utils.FindVisualChilds<ComboBox>(this, ExemptInputs).ToList();
CheckBoxInputs = Utils.FindVisualChilds<CheckBox>(this, ExemptInputs).ToList();
RadioButtonInputs = Utils.FindVisualChilds<RadioButton>(this, ExemptInputs).ToList();
foreach (var tb in TextBoxInputs)
Valid[tb] = true;
}
protected override void OnClosing(CancelEventArgs evt) {
Context.Dispose();
base.OnClosing(evt);
}
private int CountMatchesInMember(Member m) {
if (TextFilter.Count == 0) return 0;
string?[] check = new string?[] { m.MgNr.ToString(), m.FamilyName.ToLower(), m.GivenName.ToLower(), m.DefaultKg?.Name?.ToLower() };
int i = 0;
foreach (string? c in check) {
if (c == null) continue;
if (TextFilter.Any(f => c == f))
i += 10;
else if (TextFilter.Any(f => c.Contains(f)))
i += 1;
}
return i;
}
private void RefreshMemberList() {
Context.Members.Load();
IQueryable<Member> memberQuery = Context.Members;
if (ActiveMemberInput.IsChecked == true)
memberQuery = memberQuery.Where(m => m.IsActive);
List<Member> members = memberQuery.ToList();
members = members.OrderBy(m => m.FamilyName + " " + m.GivenName).ToList();
if (TextFilter.Count > 0) {
members = members
.ToDictionary(m => m, m => CountMatchesInMember(m))
.OrderByDescending(a => a.Value)
.Where(a => a.Value > 0)
.Select(a => a.Key)
.ToList();
}
MemberList.ItemsSource = members;
if (members.Count == 1)
MemberList.SelectedIndex = 0;
RefreshInputs();
}
private void ClearInputStates() {
foreach (var tb in TextBoxInputs)
Utils.ClearInputState(tb);
foreach (var cb in ComboBoxInputs)
Utils.ClearInputState(cb);
foreach (var cb in CheckBoxInputs)
Utils.ClearInputState(cb);
foreach (var rb in RadioButtonInputs)
Utils.ClearInputState(rb);
}
private void RefreshInputs(bool validate = false) {
ClearInputStates();
Member m = (Member)MemberList.SelectedItem;
if (m != null) {
EditMemberButton.IsEnabled = true;
DeleteMemberButton.IsEnabled = true;
FillInputs(m);
} else {
EditMemberButton.IsEnabled = false;
DeleteMemberButton.IsEnabled = false;
ClearInputs();
}
if (!validate) ClearInputStates();
GC.Collect();
}
private void InitInputs() {
MgNrInput.Text = Utils.NextMgNr(Context).ToString();
EntryDateInput.Text = DateTime.Now.ToString("dd.MM.yyyy");
if (Context.Branches.Count() == 1)
BranchInput.SelectedItem = Context.Branches.First();
ActiveInput.IsChecked = true;
ContactPostInput.IsChecked = true;
FillOriginalValues();
}
private void MemberList_SelectionChanged(object sender, RoutedEventArgs evt) {
RefreshInputs();
}
private void ActiveMemberInput_Changed(object sender, RoutedEventArgs evt) {
RefreshMemberList();
}
private void NewMemberButton_Click(object sender, RoutedEventArgs evt) {
IsCreating = true;
MemberList.IsEnabled = false;
MemberList.SelectedItem = null;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
InitInputs();
LockSearchInputs();
}
private void EditMemberButton_Click(object sender, RoutedEventArgs evt) {
if (MemberList.SelectedItem == null)
return;
IsEditing = true;
MemberList.IsEnabled = false;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
LockSearchInputs();
}
private void DeleteMemberButton_Click(object sender, RoutedEventArgs evt) {
Member m = (Member)MemberList.SelectedItem;
if (m == null) return;
var r = MessageBox.Show(
$"Soll das Mitglied \"{m.FamilyName} {m.GivenName}\" (MgNr. {m.MgNr}) wirklich unwiderruflich gelöscht werden?",
"Mitglied löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) {
Context.Remove(m);
Context.SaveChanges();
RefreshMemberList();
}
}
private void SaveButton_Click(object sender, RoutedEventArgs evt) {
Member? m = new();
if (IsEditing)
m = (Member)MemberList.SelectedItem;
else if (IsCreating)
m = new();
int newMgNr = int.Parse(MgNrInput.Text);
m.PredecessorMgNr = (PredecessorMgNrInput.Text == "") ? null : int.Parse(PredecessorMgNrInput.Text);
m.Prefix = (PrefixInput.Text == "") ? null : PrefixInput.Text;
m.GivenName = GivenNameInput.Text;
m.FamilyName = FamilyNameInput.Text;
m.Suffix = (SuffixInput.Text == "") ? null : SuffixInput.Text;
m.Birthday = (BirthdayInput.Text == "") ? null : string.Join("-", BirthdayInput.Text.Split(".").Reverse());
m.CountryCode = "AT";
m.PostalDestId = ((AT_PlzDest)OrtInput.SelectedItem).Id;
m.Address = AddressInput.Text;
m.Email = (EmailInput.Text == "") ? null : EmailInput.Text;
m.PhoneLandline = (PhoneLandlineInput.Text == "") ? null : PhoneLandlineInput.Text.Replace(" ", "");
m.PhoneMobile1 = (PhoneMobile1Input.Text == "") ? null : PhoneMobile1Input.Text.Replace(" ", "");
m.PhoneMobile2 = (PhoneMobile2Input.Text == "") ? null : PhoneMobile2Input.Text.Replace(" ", "");
m.Iban = (IbanInput.Text == "") ? null : IbanInput.Text.Replace(" ", "");
m.Bic = (BicInput.Text == "") ? null : BicInput.Text;
m.UstId = (UstIdInput.Text == "") ? null : UstIdInput.Text;
m.LfbisNr = (LfbisNrInput.Text == "") ? null : LfbisNrInput.Text;
m.IsBuchführend = BuchführendInput.IsChecked ?? false;
// TODO Rechnungsadresse
m.EntryDate = (EntryDateInput.Text == "") ? null : string.Join("-", EntryDateInput.Text.Split(".").Reverse());
m.ExitDate = (ExitDateInput.Text == "") ? null : string.Join("-", ExitDateInput.Text.Split(".").Reverse());
m.BusinessShares = (BusinessSharesInput.Text == "") ? 0 : int.Parse(BusinessSharesInput.Text);
m.AccountingNr = (AccountingNrInput.Text == "") ? null : AccountingNrInput.Text;
m.IsActive = ActiveInput.IsChecked ?? false;
m.IsVollLieferant = VollLieferantInput.IsChecked ?? false;
m.IsFunktionär = FunkionärInput.IsChecked ?? false;
m.ZwstId = ((Branch)BranchInput.SelectedItem).ZwstId;
m.DefaultKgNr = ((AT_Kg)DefaultKgInput.SelectedItem).KgNr;
m.Comment = (CommentInput.Text == "") ? null : CommentInput.Text;
m.DefaultContact = "post";
if (ContactPostInput.IsChecked ?? false) m.DefaultContact = "post";
if (ContactEmailInput.IsChecked ?? false) m.DefaultContact = "email";
try {
if (IsEditing)
Context.Update(m);
else if (IsCreating)
Context.Add(m);
Context.SaveChanges();
if (newMgNr != m.MgNr)
Context.Database.ExecuteSql($"UPDATE member SET mgnr = {newMgNr} WHERE mgnr = {m.MgNr}");
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Mitglied aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
}
IsEditing = false;
IsCreating = false;
MemberList.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
LockInputs();
UnlockSearchInputs();
RefreshMemberList();
}
private void ResetButton_Click(object sender, RoutedEventArgs evt) {
if (IsEditing) {
RefreshInputs();
} else if (IsCreating) {
InitInputs();
}
UpdateButtons();
}
private void CancelButton_Click(object sender, RoutedEventArgs evt) {
IsEditing = false;
IsCreating = false;
MemberList.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
RefreshInputs();
ClearInputStates();
LockInputs();
UnlockSearchInputs();
}
private void ContractButton_Click(object sender, RoutedEventArgs evt) {
var w = new ContractListWindow((Member)MemberList.SelectedItem);
w.Show();
}
private void SearchInput_TextChanged(object sender, RoutedEventArgs evt) {
TextFilter = SearchInput.Text.ToLower().Split(" ").ToList().FindAll(s => s != "");
RefreshMemberList();
}
private void Menu_Member_SendEmail_Click(object sender, RoutedEventArgs evt) {
Utils.MailTo(((Member)MemberList.SelectedItem).Email);
}
private void FocusSearchInput(object sender, RoutedEventArgs evt) {
if (!IsEditing && !IsCreating) {
SearchInput.Focus();
SearchInput.SelectAll();
}
}
private void ShowSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = true;
SaveButton.Visibility = Visibility.Visible;
ResetButton.Visibility = Visibility.Visible;
CancelButton.Visibility = Visibility.Visible;
}
private void HideSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = false;
SaveButton.Visibility = Visibility.Hidden;
ResetButton.Visibility = Visibility.Hidden;
CancelButton.Visibility = Visibility.Hidden;
}
private void ShowNewEditDeleteButtons() {
NewMemberButton.IsEnabled = true;
EditMemberButton.IsEnabled = MemberList.SelectedItem != null;
DeleteMemberButton.IsEnabled = MemberList.SelectedItem != null;
NewMemberButton.Visibility = Visibility.Visible;
EditMemberButton.Visibility = Visibility.Visible;
DeleteMemberButton.Visibility = Visibility.Visible;
}
private void HideNewEditDeleteButtons() {
NewMemberButton.IsEnabled = false;
EditMemberButton.IsEnabled = false;
DeleteMemberButton.IsEnabled = false;
NewMemberButton.Visibility = Visibility.Hidden;
EditMemberButton.Visibility = Visibility.Hidden;
DeleteMemberButton.Visibility = Visibility.Hidden;
}
private void LockInputs() {
foreach (var tb in TextBoxInputs)
tb.IsReadOnly = true;
foreach (var cb in ComboBoxInputs)
cb.IsEnabled = false;
foreach (var cb in CheckBoxInputs)
cb.IsEnabled = false;
foreach (var rb in RadioButtonInputs)
rb.IsEnabled = false;
}
private void UnlockInputs() {
foreach (var tb in TextBoxInputs)
tb.IsReadOnly = false;
foreach (var cb in ComboBoxInputs)
cb.IsEnabled = true;
foreach (var cb in CheckBoxInputs)
cb.IsEnabled = true;
foreach (var rb in RadioButtonInputs)
rb.IsEnabled = true;
}
private void LockSearchInputs() {
SearchInput.IsEnabled = false;
ActiveMemberInput.IsEnabled = false;
}
private void UnlockSearchInputs() {
SearchInput.IsEnabled = true;
ActiveMemberInput.IsEnabled = true;
}
private void FillInputs(Member m) {
OriginalValues.Clear();
MgNrInput.Text = m.MgNr.ToString();
PredecessorMgNrInput.Text = m.PredecessorMgNr.ToString();
PrefixInput.Text = m.Prefix;
GivenNameInput.Text = m.GivenName;
FamilyNameInput.Text = m.FamilyName;
SuffixInput.Text = m.Suffix;
BirthdayInput.Text = (m.Birthday != null) ? string.Join(".", m.Birthday.Split("-").Reverse()) : null;
AddressInput.Text = m.Address;
AT_PlzDest? p = m.PostalDest.AtPlz;
if (p != null) {
PlzInput.Text = p.Plz.ToString();
OrtInput.ItemsSource = p.AtPlz.Orte;
OrtInput.SelectedItem = p;
} else {
PlzInput.Text = null;
OrtInput.ItemsSource = null;
OrtInput.SelectedItem = null;
}
EmailInput.Text = m.Email;
PhoneLandlineInput.Text = m.PhoneLandline;
PhoneMobile1Input.Text = m.PhoneMobile1;
PhoneMobile2Input.Text = m.PhoneMobile2;
IbanInput.Text = m.Iban;
BicInput.Text = m.Bic;
UstIdInput.Text = m.UstId;
LfbisNrInput.Text = m.LfbisNr;
BuchführendInput.IsChecked = m.IsBuchführend;
// TODO Rechnungsadresse
EntryDateInput.Text = (m.EntryDate != null) ? string.Join(".", m.EntryDate.Split("-").Reverse()) : null;
ExitDateInput.Text = (m.ExitDate != null) ? string.Join(".", m.ExitDate.Split("-").Reverse()) : null;
BusinessSharesInput.Text = m.BusinessShares.ToString();
AccountingNrInput.Text = m.AccountingNr;
BranchInput.SelectedItem = m.Branch;
DefaultKgInput.SelectedItem = m.DefaultKg;
CommentInput.Text = m.Comment;
ActiveInput.IsChecked = m.IsActive;
VollLieferantInput.IsChecked = m.IsVollLieferant;
FunkionärInput.IsChecked = m.IsFunktionär;
switch (m.DefaultContact) {
case "post": ContactPostInput.IsChecked = true; break;
case "email": ContactEmailInput.IsChecked = true; break;
}
AreaCommitment.Text = $"{m.Contracts.Select(c => c.Area).Sum():N0} m²";
Menu_Member_SendEmail.IsEnabled = m.Email != null;
FillOriginalValues();
}
private void FillOriginalValues() {
foreach (var tb in TextBoxInputs)
OriginalValues[tb] = tb.Text;
foreach (var cb in ComboBoxInputs)
OriginalValues[cb] = cb.SelectedItem;
foreach (var cb in CheckBoxInputs)
OriginalValues[cb] = (cb.IsChecked ?? false) ? bool.TrueString : null;
foreach (var rb in RadioButtonInputs)
OriginalValues[rb] = (rb.IsChecked ?? false) ? bool.TrueString : null;
}
private void ClearInputs() {
Menu_Member_SendEmail.IsEnabled = false;
AreaCommitment.Text = "- m²";
OriginalValues.Clear();
foreach (var tb in TextBoxInputs) {
tb.Text = " ";
tb.Text = "";
}
foreach (var cb in ComboBoxInputs) {
cb.SelectedItem = null;
if (cb.ItemsSource != null)
Utils.SetInputInvalid(cb);
}
foreach (var cb in CheckBoxInputs)
cb.IsChecked = false;
foreach (var rb in RadioButtonInputs)
rb.IsChecked = false;
}
private bool IsValid() {
return Valid.All(kv => kv.Value) && ComboBoxInputs.All(cb => cb.ItemsSource == null || cb.SelectedItem != null);
}
private void UpdateButtons() {
if (!IsEditing && !IsCreating) return;
bool ch = HasChanged(), v = IsValid();
ResetButton.IsEnabled = (ch);
SaveButton.IsEnabled = (v && ch);
}
private bool InputHasChanged(Control input) {
if (!OriginalValues.ContainsKey(input)) {
return false;
} else if (input is TextBox tb) {
return OriginalValues[tb]?.ToString() != tb.Text;
} else if (input is ComboBox sb) {
return OriginalValues[sb] != sb.SelectedItem;
} else if (input is CheckBox cb) {
return (OriginalValues[cb] != null) != (cb.IsChecked ?? false);
} else if (input is RadioButton rb) {
return (OriginalValues[rb] != null) != (rb.IsChecked ?? false);
} else {
return false;
}
}
private bool HasChanged() {
return !IsValid() ||
TextBoxInputs.Any(InputHasChanged) ||
ComboBoxInputs.Any(InputHasChanged) ||
CheckBoxInputs.Any(InputHasChanged) ||
RadioButtonInputs.Any(InputHasChanged);
}
private void UpdatePlz(TextBox plzInput, ComboBox ortInput, bool optional) {
if (plzInput.Text.Length == 4) {
int plz = int.Parse(plzInput.Text);
ortInput.ItemsSource = Context.Postleitzahlen.Where(p => p.Plz == plz).ToHashSet();
} else {
ortInput.ItemsSource = null;
}
ortInput.SelectedItem = null;
if (ortInput.ItemsSource != null) {
Utils.SetInputInvalid(ortInput);
} else {
Utils.ClearInputState(ortInput);
}
Valid[plzInput] = optional || (ortInput.ItemsSource != null);
UpdateButtons();
}
private void InputTextChanged(TextBox input, bool optional, Func<TextBox, bool, ValidationResult> checker) {
InputTextChanged(input, optional, (tb, opt, ctx) => checker(tb, opt));
}
private void InputTextChanged(TextBox input, bool optional, Func<TextBox, bool, AppDbContext, ValidationResult> checker) {
InputTextChanged(input, optional, (tb, opt, ctx, m) => checker(tb, opt, ctx));
}
private void InputTextChanged(TextBox input, bool optional, Func<TextBox, bool, AppDbContext, Member, ValidationResult> checker) {
var res = checker(input, optional, Context, (Member)MemberList.SelectedItem);
Valid[input] = res.IsValid;
if (res.IsValid) {
if (InputHasChanged(input)) {
Utils.SetInputChanged(input);
} else {
Utils.ClearInputState(input);
}
} else {
Utils.SetInputInvalid(input);
}
UpdateButtons();
}
private void InputLostFocus(TextBox input, bool optional, Func<TextBox, bool, ValidationResult> checker, string? msg = null) {
InputLostFocus(input, optional, (tb, optional, ctx) => checker(tb, optional), msg);
}
private void InputLostFocus(TextBox input, bool optional, Func<TextBox, bool, AppDbContext, ValidationResult> checker, string? msg = null) {
InputLostFocus(input, optional, (tb, optional, ctx, m) => checker(tb, optional, ctx), msg);
}
private void InputLostFocus(TextBox input, bool optional, Func<TextBox, bool, AppDbContext, Member?, ValidationResult> checker, string? msg = null) {
var res = checker(input, optional, Context, (Member)MemberList.SelectedItem);
if (!res.IsValid)
MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
}
private void CheckBox_Changed(object sender, RoutedEventArgs evt) {
var input = (CheckBox)sender;
if (InputHasChanged(input)) {
Utils.SetInputChanged(input);
} else {
Utils.ClearInputState(input);
}
UpdateButtons();
}
private void RadioButton_Changed(object sender, RoutedEventArgs evt) {
var input = (RadioButton)sender;
if (InputHasChanged(input)) {
Utils.SetInputChanged(input);
} else {
Utils.ClearInputState(input);
}
UpdateButtons();
}
private void TextBox_TextChanged(object sender, RoutedEventArgs evt) {
var input = (TextBox)sender;
if (InputHasChanged(input)) {
Utils.SetInputChanged(input);
} else {
Utils.ClearInputState(input);
}
UpdateButtons();
}
private void ComboBox_SelectionChanged(object sender, RoutedEventArgs evt) {
var input = (ComboBox)sender;
if (input.ItemsSource != null && input.SelectedItem == null) {
Utils.SetInputInvalid(input);
} else if (InputHasChanged(input)) {
Utils.SetInputChanged(input);
} else {
Utils.ClearInputState(input);
}
UpdateButtons();
}
private void NumericInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, false, Validator.CheckNumeric);
}
private void MgNrInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, false, Validator.CheckMgNr);
}
private void MgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, false, Validator.CheckMgNr);
}
private void PredecessorMgNrInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckPredecessorMgNr);
}
private void PredecessorMgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, true, Validator.CheckPredecessorMgNr);
}
private void BirthdayInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckPartialDate);
}
private void PlzInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, false, Validator.CheckPlz);
UpdatePlz((TextBox)sender, OrtInput, false);
}
private void PlzInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, true, Validator.CheckPlz);
UpdatePlz((TextBox)sender, OrtInput, false);
}
private void PhoneNrInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckPhoneNumber);
}
private void PhoneNrInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, true, Validator.CheckPhoneNumber);
}
private void EmailInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckEmailAddress);
}
private void EmailInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, true, Validator.CheckEmailAddress);
}
private void IbanInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckIban);
}
private void IbanInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, true, Validator.CheckIban);
}
private void BicInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckBic);
}
private void BicInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, true, Validator.CheckBic);
}
private void UstIdInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckUstId);
}
private void UstIdInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, true, Validator.CheckUstId);
}
private void LfbisNrInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckLfbisNr);
}
private void LfbisNrInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, true, Validator.CheckLfbisNr);
}
private void BillingPlzInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckPlz);
UpdatePlz((TextBox)sender, BillingOrtInput, true);
}
private void BillingPlzInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, true, Validator.CheckPlz);
UpdatePlz((TextBox)sender, BillingOrtInput, true);
}
private void DateInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, true, Validator.CheckDate);
}
}
}

BIN
Elwig/elwig.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB