Compare commits

...

46 Commits

Author SHA1 Message Date
b6400c41c4 Bump version to 0.3.0 2023-09-17 21:31:06 +02:00
24b7078a05 Add AppDbUpdater 2023-09-17 20:48:07 +02:00
dc215d3350 BusinessDocument: Make aside table a bit more compact 2023-09-15 14:35:25 +02:00
b80cbc037c AreaComAdminWindow: Add comment input 2023-09-14 23:43:38 +02:00
4891501f62 DeliveryAdminWindow: Fix filter count check 2023-09-14 23:27:43 +02:00
6d3adc48f6 DeliveryNote: Add manual weighing reason 2023-09-14 23:25:37 +02:00
f5eea1e906 Models: Add MemberEmailAddr 2023-09-14 22:32:28 +02:00
ba691f4d17 Documents: Make business documents more compact 2023-09-14 20:44:48 +02:00
470f092482 DeliveryAdminWindow: Move manual weighing button to last position 2023-09-14 17:51:48 +02:00
e9e4c75edd Bump version to 0.2.1 2023-09-13 23:37:26 +02:00
ff1a4e7182 Documents: Add ShowFoldMarks flag 2023-09-13 23:22:35 +02:00
1e9cad6de7 DeliveryAdminWindow: Use last scale error as default manual weighing reason 2023-09-13 23:19:04 +02:00
62fe087598 DeliveryAdminWindow: Hopefully fix 'Erste Übernahme' bug 2023-09-13 22:57:21 +02:00
7f01b85878 HelpersUtilsTest: fix file encoding 2023-09-13 22:04:01 +02:00
a659d07db2 Config: add debug flag 2023-09-13 22:02:50 +02:00
b2a78907cf Bump version to 0.2.0 2023-09-12 22:52:28 +02:00
7bcf532b26 DeliveryAdminWindow: Verschlimmbesserung 2023-09-12 22:51:03 +02:00
595f9a049c ManualWeighingDialog: Do not require Reason 2023-09-12 22:51:03 +02:00
80ed90941d Include all files in Documents except .cs files 2023-09-12 21:50:56 +02:00
77cee53f2d MainWindow: Cleanup and move parts to TestWindow 2023-09-12 21:41:00 +02:00
1a673f4b3a Documents: Add CreditNote 2023-09-12 21:40:33 +02:00
5ad8c88319 Models: Add Credit 2023-09-12 21:40:04 +02:00
30aaa64f59 BusinessDocument: Add UseBillingAddress flag 2023-09-12 13:49:11 +02:00
352bf840c3 DeliveryAdminWindow: Add user confirmation on closing when creating 2023-09-10 23:03:42 +02:00
898cece0d3 DeliveryAdminWindow: Small quality of life fixes 2023-09-09 21:33:37 +02:00
0b05cc4e10 Documents: move styles 2023-09-08 13:38:26 +02:00
74fa08e95d Windows: Remove '-verwaltung' from window names 2023-09-08 13:31:16 +02:00
bc6148624c DeliveryAdminWindow: Add export option for BKI 2023-09-08 12:44:29 +02:00
f5f00a7739 Export: Add Ebics and improve Bki export 2023-09-08 00:43:53 +02:00
2de4739e9d Models: Small payment fixes 2023-09-07 16:54:33 +02:00
47e8ab7e62 Export/Bki: Add 0.0 to KMW output 2023-09-07 16:40:21 +02:00
8a678509c5 Utils: Add UTF8 encoding without BOM 2023-09-07 16:39:49 +02:00
de53bfdd2b IAddress: Add IAddress 2023-09-07 13:49:56 +02:00
f9d95a48f2 Export: Refactor interfaces and classes 2023-09-07 00:44:28 +02:00
be734b880f Models: Add payment_member 2023-09-07 00:40:53 +02:00
1c45e95ef3 Utils: Add SplitAddress and SplitName 2023-09-07 00:39:45 +02:00
7086a72fab Windows: Add cursor waiting 2023-09-06 18:04:13 +02:00
540236f878 Documents: fix page breaking issues 2023-09-06 18:00:48 +02:00
aab95ee444 Documents: Split stylesheet into multiple files 2023-09-06 17:01:59 +02:00
2b7d19199a Add WeasyPrint to convert PDFs 2023-09-06 16:01:48 +02:00
28b424fe65 Graph.cs: move from Models/ to Helpers/Billing/ 2023-09-06 14:04:14 +02:00
324c5db94e ChartWindow: use System.Text.Json instead of Newtonsoft.Json 2023-09-06 14:00:30 +02:00
faaeefe6ce Documents: Update documents 2023-09-05 20:41:03 +02:00
b76d43a5ff ClientParameters: Change GWK to Winzerkeller 2023-09-05 15:20:53 +02:00
8b48882c86 BaseDataWindow: Keep IBAN spaces when saving 2023-09-05 15:16:23 +02:00
286279b89f Fixed ChartWindow 2023-09-04 13:15:51 +02:00
66 changed files with 1946 additions and 739 deletions

View File

@ -11,6 +11,7 @@ using System.Windows.Threading;
using System.Globalization; using System.Globalization;
using System.Threading; using System.Threading;
using System.Windows.Markup; using System.Windows.Markup;
using System.Reflection;
namespace Elwig { namespace Elwig {
public partial class App : Application { public partial class App : Application {
@ -20,10 +21,23 @@ namespace Elwig {
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig"); public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
public static readonly Config Config = new(DataPath + "config.ini"); public static readonly Config Config = new(DataPath + "config.ini");
public static int VersionMajor { get; private set; }
public static int VersionMinor { get; private set; }
public static int VersionPatch { get; private set; }
public static string Version {
get => $"{VersionMajor}.{VersionMinor}.{VersionPatch}";
private set {
var p = value.Split(".").Select(p => int.Parse(p.Trim())).ToArray();
VersionMajor = p.ElementAtOrDefault(0);
VersionMinor = p.ElementAtOrDefault(1);
VersionPatch = p.ElementAtOrDefault(2);
}
}
public static string ZwstId { get; private set; } public static string ZwstId { get; private set; }
public static string BranchName { get; private set; } public static string BranchName { get; private set; }
public static int? BranchPlz { get; private set; } public static int? BranchPlz { get; private set; }
public static string? BranchOrt { get; private set; } public static string? BranchLocation { get; private set; }
public static string? BranchAddress { get; private set; } public static string? BranchAddress { get; private set; }
public static string? BranchPhoneNr { get; private set; } public static string? BranchPhoneNr { get; private set; }
public static string? BranchFaxNr { get; private set; } public static string? BranchFaxNr { get; private set; }
@ -36,7 +50,7 @@ namespace Elwig {
public App() : base() { public App() : base() {
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
Directory.CreateDirectory(App.TempPath); Directory.CreateDirectory(TempPath);
Directory.CreateDirectory(DataPath); Directory.CreateDirectory(DataPath);
MainDispatcher = Dispatcher; MainDispatcher = Dispatcher;
Scales = Array.Empty<IScale>(); Scales = Array.Empty<IScale>();
@ -56,36 +70,35 @@ namespace Elwig {
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)) new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))
); );
Version = typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "";
try {
AppDbUpdater.CheckDb();
} catch (Exception e) {
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown();
return;
}
Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = new(); Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = new();
using (var ctx = new AppDbContext()) { using (var ctx = new AppDbContext()) {
branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Dest, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
try { try {
if (!ctx.Database.CanConnect()) { Client = new(ctx);
MessageBox.Show($"Invalid Database:\n\n{Config.DatabaseFile}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown();
return;
} else {
branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Dest, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
try {
Client = new(ctx);
} catch (Exception e) {
MessageBox.Show($"Fehler beim Laden der Mandantendaten:\n\n{e.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown();
return;
}
}
} catch (Exception e) { } catch (Exception e) {
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"Fehler beim Laden der Mandantendaten:\n\n{e.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown(); Shutdown();
return; return;
} }
} }
Utils.RunBackground("HTML Initialization", () => Documents.Html.Init(PrintingReadyChanged)); Utils.RunBackground("HTML Initialization", () => Documents.Html.Init(PrintingReadyChanged));
Utils.RunBackground("PDF Initialization", () => Documents.Pdf.Init(PrintingReadyChanged)); Utils.RunBackground("PDF Initialization", () => Documents.Pdf.Init(PrintingReadyChanged));
var list = new List<IScale>(); var list = new List<IScale>();
foreach (var s in Config.Scales) { foreach (var s in Config.Scales) {
var id = s[0];
try { try {
var id = s[0];
var type = s[1]?.ToLower(); var type = s[1]?.ToLower();
var model = s[2]; var model = s[2];
var cnx = s[3]; var cnx = s[3];
@ -99,6 +112,7 @@ namespace Elwig {
throw new ArgumentException($"Invalid scale type: \"{type}\""); throw new ArgumentException($"Invalid scale type: \"{type}\"");
} }
} catch (Exception e) { } catch (Exception e) {
list.Add(new InvalidScale(id));
MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
@ -113,7 +127,7 @@ namespace Elwig {
ZwstId = entry.Item1; ZwstId = entry.Item1;
BranchName = entry.Item2; BranchName = entry.Item2;
BranchPlz = entry.Item3; BranchPlz = entry.Item3;
BranchOrt = entry.Item4; BranchLocation = entry.Item4;
BranchAddress = entry.Item5; BranchAddress = entry.Item5;
BranchPhoneNr = entry.Item6; BranchPhoneNr = entry.Item6;
BranchFaxNr = entry.Item7; BranchFaxNr = entry.Item7;
@ -124,7 +138,7 @@ namespace Elwig {
ZwstId = entry.Item1; ZwstId = entry.Item1;
BranchName = entry.Item2; BranchName = entry.Item2;
BranchPlz = entry.Item3; BranchPlz = entry.Item3;
BranchOrt = entry.Item4; BranchLocation = entry.Item4;
BranchAddress = entry.Item5; BranchAddress = entry.Item5;
BranchPhoneNr = entry.Item6; BranchPhoneNr = entry.Item6;
BranchFaxNr = entry.Item7; BranchFaxNr = entry.Item7;
@ -137,11 +151,6 @@ namespace Elwig {
base.OnStartup(evt); base.OnStartup(evt);
} }
protected override void OnExit(ExitEventArgs evt) {
Utils.RunBackground("PDF Close", () => Documents.Pdf.Close());
base.OnExit(evt);
}
private void PrintingReadyChanged() { private void PrintingReadyChanged() {
Dispatcher.BeginInvoke(OnPrintingReadyChanged, new EventArgs()); Dispatcher.BeginInvoke(OnPrintingReadyChanged, new EventArgs());
} }

View File

@ -9,24 +9,21 @@ namespace Elwig.Dialogs {
public int Weight = 0; public int Weight = 0;
public string? Reason = null; public string? Reason = null;
public ManualWeighingDialog() { public ManualWeighingDialog(string? reason = null) {
InitializeComponent(); InitializeComponent();
ReasonInput.Text = reason;
} }
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) { private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
DialogResult = true; DialogResult = true;
Weight = int.Parse(WeightInput.Text); Weight = int.Parse(WeightInput.Text);
Reason = Regex.Replace(ReasonInput.Text, @"\s+", "").Trim(); Reason = Regex.Replace(ReasonInput.Text, @"\s+", " ").Trim();
if (Reason == "") { if (Reason == "") Reason = null;
Reason = null;
} else if (!Reason.EndsWith(".") || !Reason.EndsWith("!") || !Reason.EndsWith("?")) {
Reason += ".";
}
Close(); Close();
} }
private void UpdateButtons() { private void UpdateButtons() {
ConfirmButton.IsEnabled = WeightInput.Text.Length > 0 && ReasonInput.Text.Trim().Length > 0; ConfirmButton.IsEnabled = WeightInput.Text.Length > 0;
} }
private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) { private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) {

View File

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.BusinessDocument> @inherits TemplatePage<Elwig.Documents.BusinessDocument>
@model Elwig.Documents.BusinessDocument @model Elwig.Documents.BusinessDocument
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-businessdocument.css" />
<div class="info-wrapper"> <div class="info-wrapper">
<div class="address-wrapper"> <div class="address-wrapper">
<div class="sender"> <div class="sender">
@ -14,7 +14,8 @@
<address>@Model.Address</address> <address>@Model.Address</address>
</div> </div>
<aside>@Raw(Model.Aside)</aside> <aside>@Raw(Model.Aside)</aside>
@if (Model.ShowDateAndLocation) {
<div class="date">@Model.Location, am @($"{Model.Date:dd.MM.yyyy}")</div>
}
</div> </div>
<main> @RenderBody()
@RenderBody()
</main>

View File

@ -3,17 +3,20 @@ using Elwig.Models;
namespace Elwig.Documents { namespace Elwig.Documents {
public abstract class BusinessDocument : Document { public abstract class BusinessDocument : Document {
public bool ShowDateAndLocation = false;
public Member Member; public Member Member;
public bool IncludeSender = false; public bool IncludeSender = false;
public string Aside { get; set; } public bool UseBillingAddress = false;
public string? Location { get; set; } public string Aside;
public string? Location;
public BusinessDocument(string title, Member m, bool includeSender = false) : base(title) { public BusinessDocument(string title, Member m, bool includeSender = false) : base(title) {
Member = m; Member = m;
Location = App.BranchName; Location = App.BranchLocation;
IncludeSender = includeSender; IncludeSender = includeSender;
var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>"); var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>");
Aside = $"<table><colgroup><col span='1' style='width: 2.25cm;'/><col span='1' style='width: 100%;'/></colgroup>" + Aside = $"<table><colgroup><col span='1' style='width: 22.5mm;'/><col span='1' style='width: 42.5mm;'/></colgroup>" +
$"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" + $"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" +
$"<tr><th>Mitglieds-Nr.</th><td>{m.MgNr}</td></tr>" + $"<tr><th>Mitglieds-Nr.</th><td>{m.MgNr}</td></tr>" +
$"<tr><th>Betriebs-Nr.</th><td>{m.LfbisNr}</td></tr>" + $"<tr><th>Betriebs-Nr.</th><td>{m.LfbisNr}</td></tr>" +
@ -24,10 +27,11 @@ namespace Elwig.Documents {
public string Address { public string Address {
get { get {
var b = Member.BillingAddress; var b = Member.BillingAddress;
var plz = (b == null) ? Member.PostalDest.AtPlz : b.PostalDest.AtPlz; if (b != null && UseBillingAddress) {
if (b != null) { var plz = b.PostalDest.AtPlz;
return $"{b.Name}\n{Member.AdministrativeName}\n{b.Address}\n{plz.Plz} {plz.Dest}\nÖsterreich"; return $"{b.Name}\n{Member.AdministrativeName}\n{b.Address}\n{plz.Plz} {plz.Dest}\nÖsterreich";
} else { } else {
var plz = Member.PostalDest.AtPlz;
return $"{Member.AdministrativeName}\n{Member.Address}\n{plz.Plz} {plz.Dest}\nÖsterreich"; return $"{Member.AdministrativeName}\n{Member.Address}\n{plz.Plz} {plz.Dest}\nÖsterreich";
} }
} }

View File

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

View File

@ -0,0 +1,85 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.CreditNote>
@model Elwig.Documents.CreditNote
@{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-creditnote.css"/>
@{
var bucketNum = Model.BucketNames.Length;
}
<main>
<h1>@Model.Title</h1>
<table class="credit">
<colgroup>
<col style="width: 24mm;"/>
<col style="width: 6mm;"/>
<col style="width: 31mm;"/>
<col style="width: 15mm;"/>
<col style="width: 8mm;"/>
<col style="width: 10mm;"/>
<col style="width: 12mm;"/>
<col style="width: 10mm;"/>
<col style="width: 12mm;"/>
<col style="width: 15mm;"/>
<col style="width: 17mm;"/>
</colgroup>
<thead>
<tr>
<th rowspan="3" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="3">Pos.</th>
<th rowspan="3" style="text-align: left;">Sorte</th>
<th rowspan="3" style="text-align: left;">Attribut(e)</th>
<th rowspan="2" colspan="2">Gradation</th>
<th colspan="2">Zu-/Abschläge</th>
<th colspan="2">@Raw(string.Join("<br/>", Model.BucketNames))</th>
<th rowspan="2">Betrag</th>
</tr>
<tr>
<th>Abs.</th>
<th>Rel.</th>
<th>Gewicht</th>
<th>Preis</th>
</tr>
<tr>
<th>[°Oe]</th>
<th>[°KMW]</th>
<th>[@Model.CurrencySymbol/kg]</th>
<th>[%]</th>
<th>[kg]</th>
<th>[@Model.CurrencySymbol/kg]</th>
<th>[@Model.CurrencySymbol]</th>
</tr>
</thead>
<tbody>
@{
string FormatRow(int? weight, decimal? amount) {
var w = weight == null || weight == 0 ? "-" : $"{weight:N0}";
return $"<td class='weight'>{w}</td><td class='amount'>{amount?.ToString("0." + string.Concat(Enumerable.Repeat('0', Model.Precision)))}</td>";
}
string? last = null;
}
@foreach (var part in Model.Parts) {
var pmt = part.Payment;
var abs = pmt?.ModAbs == null || pmt?.ModAbs == 0 ? "-" : pmt?.ModAbs.ToString("0." + string.Concat(Enumerable.Repeat('0', Model.Precision)));
var rel = pmt?.ModRel == null || pmt?.ModRel == 0 ? "-" : $"{pmt?.ModRel * 100:0.00##}";
<tr class="first @(bucketNum <= 1 ? "last" : "") @(last != null && last != part.SortId ? "new" : "")">
<td rowspan="@bucketNum" class="lsnr">@part.Delivery.LsNr</td>
<td rowspan="@bucketNum" class="dpnr">@part.DPNr</td>
<td rowspan="@bucketNum" class="variant">@part.Variant.Name</td>
<td rowspan="@bucketNum" class="attribute">@string.Join(" / ", part.PartAttributes.Select(a => a.AttrId))</td>
<td rowspan="@bucketNum" class="oe">@($"{part.Oe:N0}")</td>
<td rowspan="@bucketNum" class="kmw">@($"{part.Kmw:N1}")</td>
<td rowspan="@bucketNum" class="abs">@abs</td>
<td rowspan="@bucketNum" class="rel">@rel</td>
@Raw(FormatRow(pmt?.Buckets?.ElementAtOrDefault(0), pmt?.Prices?.ElementAtOrDefault(0)))
<td rowspan="@bucketNum" class="amount sum">@($"{pmt?.Amount:N2}")</td>
</tr>
@for (int i = 1; i < bucketNum; i++) {
<tr class="@(i == bucketNum - 1 ? "last" : "")">
@Raw(FormatRow(pmt?.Buckets?.ElementAtOrDefault(i), pmt?.Prices?.ElementAtOrDefault(i)))
</tr>
}
last = part.SortId;
}
</tbody>
</table>
</main>

View File

@ -0,0 +1,40 @@
using Elwig.Helpers;
using Elwig.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
namespace Elwig.Documents {
public class CreditNote : BusinessDocument {
public Credit Credit;
public string? Text;
public string CurrencySymbol;
public string[] BucketNames;
public int Precision;
public IEnumerable<DeliveryPart> Parts;
public CreditNote(Credit c, AppDbContext ctx) : base($"Traubengutschrift Nr. {c.TgId} {c.Payment.Variant.Name}", c.Member) {
UseBillingAddress = true;
ShowDateAndLocation = true;
Credit = c;
Aside = Aside.Replace("</table>", "") +
$"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" +
$"<tr><th>TG-Nr.</th><td>{c.TgId}</td></tr>" +
$"<tr><th>Überw. am</th><td>{c.Payment.Variant.TransferDate:dd.MM.yyyy}</td></tr>" +
$"<tr><th>Datum/Zeit</th><td>{c.ModifiedTimestamp:dd.MM.yyyy} / {c.ModifiedTimestamp:HH:mm}</td></tr>" +
$"</tbody></table>";
Text = App.Client.TextDeliveryNote;
DocumentId = $"Tr.-Gutschr. {c.TgId}";
CurrencySymbol = c.Payment.Variant.Season.Currency.Symbol ?? c.Payment.Variant.Season.Currency.Code;
BucketNames = c.Payment.Variant.BucketNames;
Precision = c.Payment.Variant.Season.Precision;
Parts = ctx.DeliveryParts.FromSql($"""
SELECT p.*
FROM v_delivery v
JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr)
WHERE (v.year, v.mgnr) = ({c.Year}, {c.Member.MgNr})
ORDER BY sortid, LENGTH(attributes) DESC, attributes, kmw DESC, date, time, dpnr
""").ToList();
}
}}

View File

@ -2,8 +2,8 @@
@inherits TemplatePage<Elwig.Documents.DeliveryNote> @inherits TemplatePage<Elwig.Documents.DeliveryNote>
@model Elwig.Documents.DeliveryNote @model Elwig.Documents.DeliveryNote
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-deliverynote.css" />
<div class="date">@Model.Location, am @($"{Model.Date:dd.MM.yyyy}")</div> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
@{ @{
bool displayStats = true; // Model.Delivery.Year == Model.CurrentNextSeason bool displayStats = true; // Model.Delivery.Year == Model.CurrentNextSeason
@ -11,38 +11,49 @@
<script> <script>
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const hidden = document.getElementsByClassName("hidden")[0]; const hidden = document.getElementsByClassName("hidden")[0];
const bottom = hidden.offsetTop + hidden.offsetHeight; const table = document.getElementsByClassName("delivery")[0];
const cm = bottom * 2.54 / 96 * window.devicePixelRatio; const stats = document.getElementById("delivery-stats");
const mm = px2mm(0, hidden.offsetTop + hidden.offsetHeight);
const heightTable = px2mm(table.offsetTop, hidden.offsetTop + hidden.offsetHeight);
if (cm > 25.75) { if (mm >= heightA4 - heightFooter) {
// force page break if (heightTable + 10 >= heightMain) {
const table = document.getElementsByClassName("delivery")[0]; // force page break in table
const tblOff = px2mm(0, table.offsetTop);
let last = null;
for (const tr of table.getElementsByTagName("tr")) {
if (!tr.classList.contains("main")) continue;
const mm2 = tblOff + px2mm(0, tr.offsetTop);
if (mm2 >= heightA4 - heightFooter) {
last.classList.add("page-break");
break;
}
last = tr;
}
} else {
// force page break
const hr = document.createElement("hr");
hr.classList.add("page-break");
table.before(hr);
const hr = document.createElement("hr"); const p = document.createElement("p");
hr.classList.add("page-break"); p.innerText = "Siehe nächste Seite."
table.before(hr); hr.before(p);
}
const stats = document.getElementById("delivery-stats");
stats.getElementsByTagName("table")[0].classList.add("expanded");
hr.before(stats);
const p = document.createElement("p");
p.innerText = "Siehe nächste Seite."
stats.before(p);
} }
}); });
</script> </script>
<table class="delivery"> <table class="delivery">
<colgroup> <colgroup>
<col style="width: 1cm;"/> <col style="width: 10.00mm;"/>
<col style="width: 25%;"/> <col style="width: 21.25mm;"/>
<col style="width: 25%;"/> <col style="width: 21.25mm;"/>
<col style="width: 25%;"/> <col style="width: 21.25mm;"/>
<col style="width: 25%;"/> <col style="width: 21.25mm;"/>
<col style="width: 3cm;"/> <col style="width: 30.00mm;"/>
<col style="width: 1.25cm;"/> <col style="width: 12.50mm;"/>
<col style="width: 1.25cm;"/> <col style="width: 12.50mm;"/>
<col style="width: 1.5cm;"/> <col style="width: 15.00mm;"/>
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
@ -79,7 +90,8 @@
} }
} }
<tr><td></td><td colspan="5"> <tr><td></td><td colspan="5">
@Raw(part.ManualWeighing ? "<i>Handwiegung</i>" : $"<i>Waage:</i> {part.ScaleId ?? "?"}, <i>ID:</i> {part.WeighingId ?? "?"}") (@(part.IsGerebelt ? "gerebelt gewogen" : "nicht gerebelt gewogen")) @Raw(part.ManualWeighing ? "<i>Handwiegung</i>" : $"<i>Waage:</i> {part.ScaleId ?? "?"}, <i>ID:</i> {part.WeighingId ?? "?"}")
(@(part.IsGerebelt ? "gerebelt gewogen" : "nicht gerebelt gewogen"))@Raw(part.WeighingReason != null ? $", <i>Begründung:</i>" : "") @part.WeighingReason
</td></tr> </td></tr>
@if (part.Comment != null) { @if (part.Comment != null) {
<tr><td></td><td colspan="5"><i>Anmerkung:</i> @part.Comment</td></tr> <tr><td></td><td colspan="5"><i>Anmerkung:</i> @part.Comment</td></tr>
@ -105,17 +117,17 @@
<div id="delivery-stats"> <div id="delivery-stats">
<table class="delivery-stats"> <table class="delivery-stats">
<colgroup> <colgroup>
<col style="width: 100%;"/> <col style="width: 45mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
<th><b>Gesamtlieferung</b> [kg]</th> <th><b>Lese @Model.Delivery.Year</b> per @($"{Model.Date:dd.MM.yyyy}") [kg]</th>
<th>Lieferpflicht</th> <th>Lieferpflicht</th>
<th>Lieferrecht</th> <th>Lieferrecht</th>
<th>Unterliefert</th> <th>Unterliefert</th>
@ -135,13 +147,14 @@
$"<td>{sum:N0}</td>"; $"<td>{sum:N0}</td>";
} }
var sortids = Model.Delivery.Parts.Select(p => p.SortId).ToList(); var sortids = Model.Delivery.Parts.Select(p => p.SortId).ToList();
var buckets = Model.MemberBuckets.GroupBy(b => b.Item1[..2]).ToDictionary(g => g.Key, g => g.Count());
} }
<tr> <tr>
<th>Geschäftsanteile</th> <th>Gesamtlieferung</th>
@Raw(FormatRow(Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Delivery.Year).Sum(d => d.Weight))) @Raw(FormatRow(Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Delivery.Year).Sum(d => d.Weight)))
</tr> </tr>
@foreach (var (id, name, right, obligation, sum) in Model.MemberBuckets.OrderBy(b => b.Item1)) { @foreach (var (id, name, right, obligation, sum) in Model.MemberBuckets.OrderBy(b => b.Item1)) {
if (right > 0 && obligation > 0) { if (right > 0 || obligation > 0 || (sum > 0 && buckets[id[..2]] > 1 && !id.EndsWith('_'))) {
<tr class="@(sortids.Contains(id[..2]) ? "" : "optional")"> <tr class="@(sortids.Contains(id[..2]) ? "" : "optional")">
<th>@name</th> <th>@name</th>
@Raw(FormatRow(obligation, right, sum)) @Raw(FormatRow(obligation, right, sum))
@ -152,6 +165,7 @@
</table> </table>
</div> </div>
} }
</main>
@for (int i = 0; i < 2; i++) { @for (int i = 0; i < 2; i++) {
<div class="@(i == 0 ? "hidden" : "bottom")"> <div class="@(i == 0 ? "hidden" : "bottom")">
@if (Model.Text != null) { @if (Model.Text != null) {

View File

@ -10,6 +10,8 @@ namespace Elwig.Documents {
public IEnumerable<(string, string, int, int, int)> MemberBuckets; public IEnumerable<(string, string, int, int, int)> MemberBuckets;
public DeliveryNote(Delivery d, AppDbContext ctx) : base($"Traubenübernahmeschein Nr. {d.LsNr}", d.Member) { public DeliveryNote(Delivery d, AppDbContext ctx) : base($"Traubenübernahmeschein Nr. {d.LsNr}", d.Member) {
UseBillingAddress = true;
ShowDateAndLocation = true;
Delivery = d; Delivery = d;
Aside = Aside.Replace("</table>", "") + Aside = Aside.Replace("</table>", "") +
$"<thead><tr><th colspan='2'>Lieferung</th></tr></thead><tbody>" + $"<thead><tr><th colspan='2'>Lieferung</th></tr></thead><tbody>" +

View File

@ -1,26 +1,31 @@
@using RazorLight @using RazorLight
@inherits TemplatePage<Elwig.Documents.Document> @inherits TemplatePage<Elwig.Documents.Document>
@model Elwig.Documents.Document @model Elwig.Documents.Document
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de-AT"> <html lang="de-AT">
<head> <head>
<title>@Model.Title</title> <title>@Model.Title</title>
<meta name="author" value="@Model.Author"/>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<script> <script>
window.PagedConfig = { auto: false }; const heightA4 = 297, widhtA4 = 210, heightFooter = 35, heightHeader = 25;
if (!navigator.webdriver) { const heightMain = heightA4 - heightFooter - heightHeader;
window.addEventListener("beforeprint", async () => { await window.PagedPolyfill.preview(); }); function px2mm(px1, px2) {
window.addEventListener("afterprint", () => { location.reload(); }); return (px2 - px1 + 1) * 2.54 / 96 * window.devicePixelRatio * 10;
} }
</script> </script>
<script src="@Raw(Model.DataPath)\resources\paged.polyfill.js"></script> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style.css"/>
<link rel="stylesheet" href="@Raw(Model.DataPath)\resources\style.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-page.css"/>
</head> </head>
<body> <body>
<div class="m1"></div> @if (Model.ShowFoldMarks) {
<div class="m2"></div> <div class="m1"></div>
<div class="m3"></div> <div class="m2"></div>
<div class="m3"></div>
<div class="m1 r"></div>
<div class="m2 r"></div>
<div class="m3 r"></div>
}
<div class="footer-wrapper"> <div class="footer-wrapper">
<div class="pre-footer"> <div class="pre-footer">
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span> <span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>

View File

@ -8,12 +8,24 @@ namespace Elwig.Documents {
private TempFile? PdfFile = null; private TempFile? PdfFile = null;
public bool ShowFoldMarks = App.Config.Debug;
public string DataPath;
public int CurrentNextSeason;
public string? DocumentId;
public string Title;
public string Author;
public string Header;
public string Footer;
public DateTime Date;
public Document(string title) { public Document(string title) {
var c = App.Client; var c = App.Client;
DataPath = App.DataPath; DataPath = App.DataPath;
CurrentNextSeason = Utils.CurrentNextSeason; CurrentNextSeason = Utils.CurrentNextSeason;
Title = title; Title = title;
Header = $"<h1>{c.Name}</h1>"; Author = c.NameFull;
Header = $"<div class='name'>{c.Name}</div><div class='suffix'>{c.NameSuffix}</div>";
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ") Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
.Item(c.NameFull).NextLine() .Item(c.NameFull).NextLine()
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine() .Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
@ -33,33 +45,30 @@ namespace Elwig.Documents {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public string DataPath { get; set; } private Task<string> Render() {
public int CurrentNextSeason { get; set; }
public string Title { get; set; }
public string Header { get; set; }
public string Footer { get; set; }
public DateTime Date { get; set; }
public string? DocumentId { get; set; }
private async Task<string> Render() {
string name; string name;
if (this is BusinessLetter) { if (this is BusinessLetter) {
name = "BusinessLetter"; name = "BusinessLetter";
} else if (this is DeliveryNote) { } else if (this is DeliveryNote) {
name = "DeliveryNote"; name = "DeliveryNote";
} else if (this is CreditNote) {
name = "CreditNote";
} else { } else {
throw new InvalidOperationException("Invalid document object"); throw new InvalidOperationException("Invalid document object");
} }
return await Html.CompileRenderAsync(name, this); return Render(name);
}
private Task<string> Render(string name) {
return Html.CompileRenderAsync(name, this);
} }
public async Task Generate() { public async Task Generate() {
var pdf = new TempFile("pdf"); var pdf = new TempFile("pdf");
using (var tmpHtml = new TempFile("html")) { using (var tmpHtml = new TempFile("html")) {
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render()); await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath); await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath);
} }
Pdf.UpdateMetadata(pdf.FilePath, Title, App.Client.NameFull);
PdfFile = pdf; PdfFile = pdf;
} }

View File

@ -18,6 +18,7 @@ namespace Elwig.Documents {
await e.CompileTemplateAsync("BusinessDocument"); await e.CompileTemplateAsync("BusinessDocument");
await e.CompileTemplateAsync("BusinessLetter"); await e.CompileTemplateAsync("BusinessLetter");
await e.CompileTemplateAsync("DeliveryNote"); await e.CompileTemplateAsync("DeliveryNote");
await e.CompileTemplateAsync("CreditNote");
Engine = e; Engine = e;
evtHandler(); evtHandler();

View File

@ -1,64 +1,45 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using PdfSharp.Pdf.IO;
using PuppeteerSharp;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Windows; using Elwig.Windows;
using System.Diagnostics; using System.Diagnostics;
using Balbarak.WeasyPrint;
using System;
using System.IO;
namespace Elwig.Documents { namespace Elwig.Documents {
public static class Pdf { public static class Pdf {
private static readonly string Chromium = @"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe";
private static readonly string PdfToPrinter = App.ExePath + "PDFtoPrinter.exe"; private static readonly string PdfToPrinter = App.ExePath + "PDFtoPrinter.exe";
private static IBrowser? Browser = null; private static readonly FilesManager WeasyPrintManager = new();
public static bool IsReady => Browser != null; private static string? WeasyPrintPython = null;
private static string? WeasyPrintDir => WeasyPrintManager.FolderPath;
public static bool IsReady => WeasyPrintPython != null && WeasyPrintDir != null;
public static async Task Init(Action evtHandler) { public static async Task Init(Action evtHandler) {
Browser = await Puppeteer.LaunchAsync(new LaunchOptions { if (!WeasyPrintManager.IsFilesExsited()) {
Headless = true, await WeasyPrintManager.InitFilesAsync();
ExecutablePath = Chromium, }
// paged.js uses XHRs to load styles, so this is needed WeasyPrintPython = Path.Combine(WeasyPrintManager.FolderPath, "python.exe");
Args = new[] { "--allow-file-access-from-files" },
});
evtHandler(); evtHandler();
} }
public static async Task Close() {
if (Browser == null) return;
await Browser.CloseAsync();
Browser = null;
}
public static async Task Convert(string htmlPath, string pdfPath) { public static async Task Convert(string htmlPath, string pdfPath) {
if (Browser == null) throw new InvalidOperationException("The puppeteer engine has not been initialized yet"); var p = new Process() { StartInfo = new() {
using var page = await Browser.NewPageAsync(); FileName = WeasyPrintPython,
page.Console += OnConsole; CreateNoWindow = true,
await page.GoToAsync($"file://{htmlPath}"); WorkingDirectory = WeasyPrintDir,
await page.EvaluateFunctionAsync("async () => { await window.PagedPolyfill.preview(); }"); RedirectStandardError = true,
await page.PdfAsync(pdfPath, new() { } };
PreferCSSPageSize = true, p.StartInfo.EnvironmentVariables["PATH"] = "Scripts;gtk3;" + Environment.GetEnvironmentVariable("PATH");
//Format = PaperFormat.A4, p.StartInfo.ArgumentList.Add("scripts/weasyprint.exe");
DisplayHeaderFooter = false, p.StartInfo.ArgumentList.Add("-e");
MarginOptions = new() { p.StartInfo.ArgumentList.Add("utf8");
Top = "0mm", p.StartInfo.ArgumentList.Add(htmlPath);
Right = "0mm", p.StartInfo.ArgumentList.Add(pdfPath);
Bottom = "0mm", p.Start();
Left = "0mm", await p.WaitForExitAsync();
}, var stderr = await p.StandardError.ReadToEndAsync();
}); if (p.ExitCode != 0) throw new Exception(stderr);
}
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) { public static void Show(TempFile file, string title) {

View File

@ -0,0 +1,142 @@
.address-wrapper, aside, main {
overflow: hidden;
}
.spacing {
height: 20mm;
}
.info-wrapper {
width: 100%;
height: 45mm;
margin: 0 0 2mm 0;
position: relative;
}
.info-wrapper .date {
text-align: right;
position: absolute;
right: 0;
bottom: -1.5em;
}
.address-wrapper {
height: 45mm;
width: 85mm;
margin: 0;
padding: 5mm;
position: absolute;
left: -5mm;
top: 0;
}
.address-wrapper .sender {
height: 4em;
font-size: 8pt;
padding: 1em 0;
}
address {
height: 5em;
white-space: pre-line;
font-size: 12pt;
font-style: normal;
}
aside {
height: 40mm;
width: 75mm;
margin: 0;
position: absolute;
left: 100mm;
top: 5mm;
}
aside table {
border-collapse: collapse;
border: 0.5pt solid #808080;
width: 65mm;
margin-right: 10mm;
}
aside table thead:not(:first-child) tr {
border-top: 0.5pt solid #808080;
}
aside table thead th {
background-color: #E0E0E0;
font-size: 10pt;
}
aside table tbody th,
aside table tbody td {
text-align: left;
font-size: 10pt;
}
aside table tbody th {
padding: 0.25mm 0.5mm 0.25mm 1mm;
}
aside table tbody td {
padding: 0.25mm 0;
}
aside table tbody th {
font-weight: normal;
}
main {
margin: 2em 0 1em 0;
}
main > *:first-child {
margin-top: 0;
}
main h1, .main-wrapper p {
font-size: 12pt;
margin: 1em 0;
text-align: justify;
}
.main-wrapper p {
widows: 3;
orphans: 3;
hyphens: manual;
}
main h1 {
margin-top: 0;
margin-bottom: 2em;
}
.main-wrapper p.comment {
font-size: 10pt;
}
.main-wrapper .hidden {
break-before: avoid;
}
.main-wrapper .bottom {
bottom: 0;
position: absolute;
width: 165mm;
}
.main-wrapper .signatures {
width: 100%;
display: flex;
justify-content: space-around;
margin: 20mm 0 2mm 0;
}
.main-wrapper .signatures > * {
width: 50mm;
border-top: 0.5pt solid black;
padding-top: 1mm;
text-align: center;
font-size: 10pt;
}

View File

@ -0,0 +1,64 @@
table.credit {
font-size: 10pt;
}
table.credit th,
table.credit td {
padding: 0 0.25mm;
}
table.credit thead {
font-size: 8pt
}
table.credit thead th {
font-weight: normal;
font-style: italic;
vertical-align: bottom;
}
table.credit td {
vertical-align: top;
}
table.credit .oe,
table.credit .kmw {
text-align: center;
}
table.credit .dpnr {
text-align: center;
}
table.credit .amount,
table.credit .weight {
text-align: right;
}
table.credit .rel,
table.credit .abs {
text-align: center;
}
table.credit .amount.sum {
vertical-align: bottom;
padding-bottom: 1mm;
}
table.credit tbody tr:not(.first):not(.last) {
break-before: avoid;
break-after: avoid;
}
table.credit tbody tr.first td {
padding-top: 1mm;
}
table.credit tbody tr.last td {
padding-bottom: 1mm;
}
table.credit tbody tr.new {
border-top: 0.5pt solid black;
}

View File

@ -0,0 +1,99 @@
main h1 {
margin-bottom: 1.5em !important;
}
table.delivery {
margin-bottom: 5mm;
}
table.delivery tr:not(.main) {
break-before: avoid;
}
table.delivery th {
font-weight: normal;
font-style: italic;
font-size: 10pt;
}
table.delivery th.main {
text-align: left;
}
table.delivery tr.main td {
font-weight: bold;
padding-top: 2mm;
}
table.delivery tbody tr:not(.main) td {
font-size: 8pt;
}
table.delivery tr.tight:not(.first) td {
padding-top: 0;
padding-bottom: 0;
}
table.delivery tr.tight.first td {
padding-bottom: 0;
}
/* FIXME update version of WeasyPrint
table.delivery tr.tight:has(+ tr:not(.tight)) td {
padding-bottom: 0.5mm !important;
}
*/
table.delivery tr.sum {
border-top: 0.5pt solid black;
break-before: avoid;
}
table.delivery tr.sum td {
padding-top: 1mm;
}
table.delivery-stats {
font-size: 8pt;
break-inside: avoid;
break-after: avoid;
}
table.delivery-stats.expanded th,
table.delivery-stats.expanded td {
padding: 0.25mm 0;
}
table.delivery-stats:not(.expanded) th,
table.delivery-stats:not(.expanded) td {
padding: 0.125mm 0;
}
table.delivery-stats:not(.expanded) tr.optional {
display: none;
}
table.delivery-stats thead th {
font-weight: normal;
font-style: italic;
text-align: right;
}
table.delivery-stats thead th:first-child {
text-align: left;
}
table.delivery-stats.expanded tbody {
font-size: 10pt;
}
table.delivery-stats td {
text-align: right;
}
table.delivery-stats tbody th {
font-weight: normal;
font-style: italic;
text-align: left;
}

View File

@ -0,0 +1,71 @@
.m1, .m2, .m3 {
height: 0;
width: 10mm;
position: fixed;
left: -25mm;
border-top: 0.5pt solid black;
}
.m1.r, .m2.r, .m3.r {
left: initial;
right: -20mm;
}
.m1 {top: 80mm;}
.m2 {top: 123.5mm;}
.m3 {top: 185mm;}
.page-break {
break-before: page;
}
hr.page-break {
display: none;
}
@page {
size: A4;
margin: 25mm 20mm 35mm 25mm;
@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: 0 20mm 40mm 25mm;
}
.footer-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
}
}
@media print {
.page::after {
content: "Seite " counter(page) " von " counter(pages) !important;
}
}

View File

@ -12,75 +12,10 @@ body {
margin: 0; margin: 0;
} }
.m1, .m2, .m3 { table {
height: 0;
width: 1cm;
position: fixed;
left: 0;
border-top: 1pt solid black;
}
.m1 {top: 105mm;}
.m2 {top: 148.5mm;}
.m3 {top: 210mm;}
header, .address-wrapper, aside, main {
overflow: hidden;
}
header {
height: 45mm;
padding: 5mm;
position: absolute;
top: -25mm;
left: 0;
right: 0;
text-align: center;
}
header h1{
font-size: 18pt;
margin-top: 1cm;
}
.spacing {
height: 20mm;
}
.info-wrapper {
width: 100%; width: 100%;
height: 45mm; border-collapse: collapse;
margin: 0 0 8.46mm 0; table-layout: fixed;
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;
} }
table td, table td,
@ -88,90 +23,45 @@ table th {
padding: 0.5mm 1mm; padding: 0.5mm 1mm;
} }
aside { table th {
height: 40mm; text-align: center;
width: 75mm; }
margin: 0;
header {
height: 45mm;
padding: 10mm 0 0 0;
position: absolute; position: absolute;
left: 125mm; top: -25mm;
top: 5mm; left: 0;
right: 0;
text-align: center;
overflow: hidden;
} }
aside table { header .name {
border-collapse: collapse; font-size: 18pt;
border: 1pt solid #808080; margin-top: 10mm;
width: calc(100% - 1cm); font-weight: bold;
margin-right: 1cm;
} }
aside table thead:not(:first-child) tr { header .suffix {
border-top: 1pt solid #808080;
}
aside table thead th {
background-color: #E0E0E0;
font-size: 10pt;
}
aside table tbody th,
aside table tbody td {
text-align: left;
font-size: 10pt;
}
aside table tbody th {
font-weight: normal;
}
main {
margin: 8.46mm 20mm 4.23mm 25mm;
}
main :first-child {
margin-top: 0;
}
main h1, main p {
font-size: 12pt; font-size: 12pt;
margin: 1em 0; font-weight: bold;
text-align: justify;
}
main p {
widows: 3;
orphans: 3;
hyphens: auto;
}
main .date {
margin-bottom: 2em;
text-align: right;
}
main h1 {
margin-bottom: 2em;
}
main p.comment {
font-size: 10pt;
} }
.footer-wrapper { .footer-wrapper {
padding: 0 20mm 0 25mm;
position: running(page-footer); position: running(page-footer);
bottom: 0; width: 165mm;
left: 0;
right: 0;
} }
.pre-footer { .pre-footer {
margin: 4.23mm 0; margin: 1em 0;
font-size: 10pt; font-size: 10pt;
display: flex;
} }
.pre-footer > * { .pre-footer > * {
flex: 5cm 1 1; display: inline-block;
width: 33%;
} }
.pre-footer .date { .pre-footer .date {
@ -185,183 +75,27 @@ main p.comment {
.pre-footer .page { .pre-footer .page {
text-align: right; text-align: right;
float: right;
} }
.pre-fotter .page::after { .pre-footer .page::after {
content: "Seite 1 von 1"; content: "Seite 1 von 1";
} }
footer { footer {
font-size: 10pt; font-size: 10pt;
border-top: 1pt solid black; border-top: 0.5pt solid black;
height: 25mm; height: 25mm;
padding-top: 1mm; padding-top: 1mm;
text-align: center; text-align: center;
} }
table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
table.delivery {
margin-bottom: 5mm;
}
table.delivery th {
font-weight: normal;
font-style: italic;
font-size: 10pt;
}
table.delivery th.main {
text-align: left;
}
table.delivery tr.main td {
font-weight: bold;
padding-top: 2mm;
}
table.delivery tbody tr:not(.main) td {
font-size: 8pt;
}
table.delivery tr.tight:not(.first) td {
padding-top: 0;
padding-bottom: 0;
}
table.delivery tr.tight.first td {
padding-bottom: 0;
}
table.delivery tr.tight:has(+ tr:not(.tight)) td {
padding-bottom: 0.5mm !important;
}
table.delivery tr.sum {
border-top: 1pt solid black;
}
table.delivery tr.sum td {
padding-top: 1mm;
}
table.delivery-stats {
font-size: 8pt;
}
table.delivery-stats.expanded th,
table.delivery-stats.expanded td {
padding: 0.25mm 0;
}
table.delivery-stats:not(.expanded) th,
table.delivery-stats:not(.expanded) td {
padding: 0.125mm 0;
}
table.delivery-stats:not(.expanded) tr.optional {
display: none;
}
table.delivery-stats thead th {
font-weight: normal;
font-style: italic;
text-align: right;
}
table.delivery-stats thead th:first-child {
text-align: left;
}
table.delivery-stats.expanded tbody {
font-size: 10pt;
}
table.delivery-stats td {
text-align: right;
}
table.delivery-stats tbody th {
font-weight: normal;
font-style: italic;
text-align: left;
}
.hidden { .hidden {
visibility: hidden; visibility: hidden;
} }
main .bottom {
bottom: 0;
position: absolute;
width: calc(100% - 25mm - 20mm);
}
main .signatures {
width: 100%;
display: flex;
justify-content: space-around;
margin: 20mm 0 2mm 0;
}
main .signatures > * {
width: 5cm;
border-top: 1pt solid black;
padding-top: 1mm;
text-align: center;
font-size: 10pt;
}
hr { hr {
border: none; border: none;
border-top: 1pt solid black; border-top: 0.5pt solid black;
margin: 5mm 0; margin: 5mm 0;
} }
hr.page-break {
display: none;
break-after: page;
}
@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: 1pt 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;
}
}

View File

@ -7,7 +7,7 @@
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>elwig.ico</ApplicationIcon> <ApplicationIcon>elwig.ico</ApplicationIcon>
<Version>0.1.0</Version> <Version>0.3.0</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
</PropertyGroup> </PropertyGroup>
@ -16,14 +16,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Balbarak.WeasyPrint" Version="2.0.2" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" />
<PackageReference Include="ini-parser" Version="2.5.2" /> <PackageReference Include="ini-parser" Version="2.5.2" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.21" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.21" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1938.49" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1938.49" />
<PackageReference Include="PdfSharp" Version="1.50.5147" />
<PackageReference Include="PuppeteerSharp" Version="11.0.2" />
<PackageReference Include="RazorLight" Version="2.3.1" /> <PackageReference Include="RazorLight" Version="2.3.1" />
<PackageReference Include="ScottPlot.WPF" Version="4.1.67" /> <PackageReference Include="ScottPlot.WPF" Version="4.1.67" />
<PackageReference Include="System.IO.Ports" Version="7.0.0" /> <PackageReference Include="System.IO.Ports" Version="7.0.0" />

View File

@ -43,6 +43,8 @@ namespace Elwig.Helpers {
public DbSet<DeliveryPartAttr> DeliveryPartAttributes { get; private set; } public DbSet<DeliveryPartAttr> DeliveryPartAttributes { get; private set; }
public DbSet<DeliveryPartModifier> DeliveryPartModifiers { get; private set; } public DbSet<DeliveryPartModifier> DeliveryPartModifiers { get; private set; }
public DbSet<PaymentVar> PaymentVariants { get; private set; } public DbSet<PaymentVar> PaymentVariants { get; private set; }
public DbSet<PaymentMember> MemberPayments { get; private set; }
public DbSet<Credit> Credits { get; private set; }
private readonly StreamWriter? LogFile = null; private readonly StreamWriter? LogFile = null;
public static DateTime LastWriteTime => File.GetLastWriteTime(App.Config.DatabaseFile); public static DateTime LastWriteTime => File.GetLastWriteTime(App.Config.DatabaseFile);

View File

@ -0,0 +1,78 @@
using Microsoft.Data.Sqlite;
using System;
namespace Elwig.Helpers {
public static class AppDbUpdater {
public static readonly int RequiredSchemaVersion = 1;
private static int _versionOffset = 0;
private static readonly Action<SqliteConnection>[] _updaters = new[] {
UpdateDbSchema_1_To_2, UpdateDbSchema_2_To_3
};
private static void ExecuteNonQuery(SqliteConnection cnx, string sql) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
private static object? ExecuteScalar(SqliteConnection cnx, string sql) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = sql;
return cmd.ExecuteScalar();
}
public static string CheckDb() {
using var cnx = AppDbContext.Connect();
var applId = (long?)ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
if (applId != 0x454C5747) throw new Exception("Invalid application_id of database");
var schemaVers = (long?)ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
_versionOffset = (int)(schemaVers % 100);
if (_versionOffset != 0) {
// schema was modified manually/externally
// TODO issue warning
}
UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
var userVers = (long?)ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
var major = userVers >> 24;
var minor = (userVers >> 16) & 0xFF;
var patch = userVers & 0xFFFF;
if (App.VersionMajor > major || App.VersionMinor > minor || App.VersionPatch > patch) {
long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch;
ExecuteNonQuery(cnx, $"PRAGMA user_version = {vers}");
}
return $"{major}.{minor}.{patch}";
}
private static void UpdateDbSchema(SqliteConnection cnx, int fromVersion, int toVersion) {
if (fromVersion == toVersion) {
return;
} else if (fromVersion > toVersion) {
throw new Exception("schema_version of database is too new");
} else if (toVersion - 1 > _updaters.Length) {
throw new Exception("Unable to update database schema: Updater not implemented");
} else if (fromVersion <= 0) {
throw new Exception("schema_version of database is invalid");
}
ExecuteNonQuery(cnx, "PRAGMA locking_mode = EXCLUSIVE");
ExecuteNonQuery(cnx, "BEGIN EXCLUSIVE");
for (int i = fromVersion; i < toVersion; i++) {
_updaters[i - 1](cnx);
}
ExecuteNonQuery(cnx, "COMMIT");
ExecuteNonQuery(cnx, "VACUUM");
ExecuteNonQuery(cnx, $"PRAGMA schema_version = {toVersion * 100 + _versionOffset}");
}
private static void UpdateDbSchema_1_To_2(SqliteConnection cnx) { }
private static void UpdateDbSchema_2_To_3(SqliteConnection cnx) { }
}
}

View File

@ -1,14 +1,10 @@
using Newtonsoft.Json.Linq;
using ScottPlot; using ScottPlot;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq; using System.Linq;
using System.Text; using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Windows.Markup;
namespace Elwig.Models { namespace Elwig.Helpers.Billing {
public class Graph : ICloneable { public class Graph : ICloneable {
public string Type { get; set; } public string Type { get; set; }
@ -30,7 +26,7 @@ namespace Elwig.Models {
DataY = DataGen.Zeros(MaxX - MinX + 1); DataY = DataGen.Zeros(MaxX - MinX + 1);
} }
public Graph(string type, int num, JToken graphData, string contracts, int minX, int maxX) { public Graph(string type, int num, JsonObject graphData, string contracts, int minX, int maxX) {
Type = type; Type = type;
Num = num; Num = num;
Contracts = contracts; Contracts = contracts;
@ -52,8 +48,8 @@ namespace Elwig.Models {
DataY = dataY; DataY = dataY;
} }
private void ParseGraphData(JToken graphData) { private void ParseGraphData(JsonObject graphData) {
var GraphPoints = graphData.Children().OfType<JProperty>().ToDictionary(p => int.Parse(p.Name[..^2]), p => (double)p.Value); var GraphPoints = graphData.ToDictionary(p => int.Parse(p.Key[..^2]), p => (double)p.Value?.AsValue());
if (GraphPoints.Keys.Count < 1) { if (GraphPoints.Keys.Count < 1) {
return; return;
@ -99,19 +95,19 @@ namespace Elwig.Models {
} }
} }
public JObject ToJson() { public JsonObject ToJson() {
JObject graph = new(); JsonObject graph = new();
if (DataY[0] != DataY[1]) { if (DataY[0] != DataY[1]) {
graph.Add(new JProperty(DataX[0] + Type.ToLower(), Math.Round(DataY[0], 4))); graph.Add(new KeyValuePair<string, JsonNode?>(DataX[0] + Type.ToLower(), Math.Round(DataY[0], 4)));
} }
for (int i = 1; i < DataX.Length - 1; i++) { for (int i = 1; i < DataX.Length - 1; i++) {
if (Math.Round(DataY[i] - DataY[i - 1], 4) != Math.Round(DataY[i + 1] - DataY[i], 4)) { if (Math.Round(DataY[i] - DataY[i - 1], 4) != Math.Round(DataY[i + 1] - DataY[i], 4)) {
graph.Add(new JProperty(DataX[i] + Type.ToLower(), Math.Round(DataY[i], 4))); graph.Add(new KeyValuePair<string, JsonNode?>(DataX[i] + Type.ToLower(), Math.Round(DataY[i], 4)));
} }
} }
if (DataY[^1] != DataY[^2]) { if (DataY[^1] != DataY[^2]) {
graph.Add(new JProperty(DataX[^1] + Type.ToLower(), Math.Round(DataY[^1], 4))); graph.Add(new KeyValuePair<string, JsonNode?>(DataX[^1] + Type.ToLower(), Math.Round(DataY[^1], 4)));
} }
return graph; return graph;
} }

View File

@ -0,0 +1,32 @@
using Elwig.Models;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Elwig.Helpers.Billing {
public class Transaction {
public readonly Member Member;
public readonly long AmountCent;
public readonly string Currency;
public readonly int Nr;
public Transaction(Member m, decimal amount, string currency, int nr) {
Member = m;
AmountCent = (long)Math.Round(amount * 100);
Currency = currency;
Nr = nr;
}
public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) {
var last = variant.Season.PaymentVariants.Where(v => v.TransferDate != null).OrderBy(v => v.TransferDate).LastOrDefault();
var dict = last?.MemberPayments.ToDictionary(m => m.MgNr, m => m.Amount) ?? new();
return variant.Credits
.OrderBy(c => c.MgNr)
.Select(c => new Transaction(c.Member, c.Amount, variant.Season.CurrencyCode, c.TgNr))
.ToList();
}
public static string FormatAmountCent(long cents) => $"{cents / 100}.{cents % 100:00}";
}
}

View File

@ -7,10 +7,13 @@ using System.Threading.Tasks;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public class ClientParameters { public class ClientParameters {
public enum Type { Matzen, GWK }; public enum Type { Matzen, Winzerkeller };
public bool IsMatzen => Client == Type.Matzen; public bool IsMatzen => Client == Type.Matzen;
public bool IsGWK => Client == Type.GWK; public bool IsWinzerkeller => Client == Type.Winzerkeller;
public bool IsWolkersdorf => Client == Type.Winzerkeller && App.ZwstId == "W";
public bool IsHaugsdorf => Client == Type.Winzerkeller && App.ZwstId == "H";
public bool IsSitzendorf => Client == Type.Winzerkeller && App.ZwstId == "S";
public string NameToken; public string NameToken;
public string NameShort; public string NameShort;
@ -61,7 +64,7 @@ namespace Elwig.Helpers {
NameType = parameters["CLIENT_NAME_TYPE"] ?? throw new KeyNotFoundException(); NameType = parameters["CLIENT_NAME_TYPE"] ?? throw new KeyNotFoundException();
switch (Name) { switch (Name) {
case "Winzergenossenschaft für Matzen und Umgebung": Client = Type.Matzen; break; case "Winzergenossenschaft für Matzen und Umgebung": Client = Type.Matzen; break;
case "Winzerkeller im Weinviertel": Client = Type.GWK; break; case "Winzerkeller im Weinviertel": Client = Type.Winzerkeller; break;
}; };
Plz = int.Parse(parameters["CLIENT_PLZ"] ?? ""); Plz = int.Parse(parameters["CLIENT_PLZ"] ?? "");

View File

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using IniParser; using IniParser;
using IniParser.Model; using IniParser.Model;
@ -9,6 +8,7 @@ namespace Elwig.Helpers {
public class Config { public class Config {
private readonly string FileName; private readonly string FileName;
public bool Debug;
public string DatabaseFile = App.DataPath + "database.sqlite3"; public string DatabaseFile = App.DataPath + "database.sqlite3";
public string? DatabaseLog = null; public string? DatabaseLog = null;
public string? Branch = null; public string? Branch = null;
@ -25,7 +25,7 @@ namespace Elwig.Helpers {
var parser = new FileIniDataParser(); var parser = new FileIniDataParser();
IniData? ini = null; IniData? ini = null;
try { try {
ini = parser.ReadFile(FileName, Encoding.UTF8); ini = parser.ReadFile(FileName, Utils.UTF8);
} catch {} } catch {}
if (ini == null || !ini.TryGetKey("database.file", out string db)) { if (ini == null || !ini.TryGetKey("database.file", out string db)) {
@ -50,6 +50,13 @@ namespace Elwig.Helpers {
Branch = branch; Branch = branch;
} }
if (ini == null || !ini.TryGetKey("general.debug", out string debug)) {
Debug = false;
} else {
debug = debug.ToLower();
Debug = debug == "1" || debug == "true" || debug == "yes" || debug == "on";
}
ScaleList.Clear(); ScaleList.Clear();
Scales = ScaleList; Scales = ScaleList;
if (ini != null) { if (ini != null) {
@ -70,9 +77,10 @@ namespace Elwig.Helpers {
} }
public void Write() { public void Write() {
using var file = new StreamWriter(FileName, false, Encoding.UTF8); using var file = new StreamWriter(FileName, false, Utils.UTF8);
file.Write($"\r\n[general]\r\n"); file.Write($"\r\n[general]\r\n");
if (Branch != null) file.Write($"branch = {Branch}\r\n"); if (Branch != null) file.Write($"branch = {Branch}\r\n");
if (Debug) file.Write("debug = true\r\n");
file.Write($"\r\n[database]\r\nfile = {DatabaseFile}\r\n"); file.Write($"\r\n[database]\r\nfile = {DatabaseFile}\r\n");
if (DatabaseLog != null) file.Write($"log = {DatabaseLog}\r\n"); if (DatabaseLog != null) file.Write($"log = {DatabaseLog}\r\n");
foreach (var s in ScaleList) { foreach (var s in ScaleList) {

View File

@ -1,4 +1,67 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public class Bki {
using Row = Tuple<(string?, string?, string?, string?, string, int, string, int), (string, int, string, string, string, int, string, double, double)>;
public class Bki : Csv<Row> {
private readonly string _clientData;
public Bki(string filename) : base(filename, ';', Encoding.Latin1) {
Header = """
EDV-Liste zum automatischen Einlesen in die Weindatenbank;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Stammdaten;;;;;;;;;;;;;;Transportschein;;;;;;;;;;;;;;;;
Empfänger:;;;;;;;Versender:;;;;;;;;;;;;;;;;;;;;;;;
Betriebsnr;Name od. Firmenname;Vorname;Straße;Hausnr;Plz;Ort;Betriebsnr;Name od. Firmenname;Vorname;Straße;Hausnr;Plz;Ort;Datum der Lieferung;Menge in kg;Art;Weiß;Rot;Sorte1;Sorte2;Sorte3;Qualitätsstufe;Jahrgang;Herkunft;°KMW;°Oe;Vollablieferer;Ha Gesamt;Ha Ertragsfähig;Flächenbindung in Ha für AMA
""";
var c = App.Client;
var (a1, a2) = Utils.SplitAddress(c.Address);
_clientData = $"{c.LfbisNr};{c.NameFull};;{a1};{a2};{c.Plz};{c.Ort}";
}
public async Task ExportAsync(int year) {
using var cnx = await AppDbContext.ConnectAsync();
using var cmd = cnx.CreateCommand();
cmd.CommandText = $"""
SELECT lfbis_nr, family_name, name, billing_name, address, plz, ort, area,
date, weight, type, sortid, qualid, year, hkid, kmw, oe
FROM v_bki_delivery
WHERE year = {year}
""";
var r = await cmd.ExecuteReaderAsync();
List<Row> rows = new();
while (await r.ReadAsync()) {
rows.Add(new(
(r.IsDBNull(0) ? null : r.GetString(0), r.IsDBNull(1) ? null : r.GetString(1), r.IsDBNull(2) ? null : r.GetString(2), r.IsDBNull(3) ? null : r.GetString(3), r.GetString(4), r.GetInt32(5), r.GetString(6), r.GetInt32(7)),
(r.GetString(8), r.GetInt32(9), r.GetString(10), r.GetString(11), r.GetString(12), r.GetInt32(13), r.GetString(14), r.GetDouble(15), r.GetDouble(16))
));
}
await ExportAsync(rows);
}
public void Export(int year) {
ExportAsync(year).GetAwaiter().GetResult();
}
public override string FormatRow(Row row) {
var (member, delivery) = row;
var (lfBisNr, familyName, name, billingName, address, plz, ort, area) = member;
var (date, weight, type, sortid, qualid, year, hkid, kmw, oe) = delivery;
var (n1, n2) = billingName == null ? (familyName, name) : Utils.SplitName(billingName, familyName);
var (a1, a2) = Utils.SplitAddress(address);
var memberData = $"{lfBisNr};{n1};{n2};{a1};{a2};{plz};{ort}";
var deliveryData = $"{string.Join(".", date.Split("-").Reverse())};{weight};TB;{(type == "W" ? "J" : "")};{(type == "R" ? "J" : "")};{sortid};;;{qualid};{year};{hkid};{kmw:0.0};{oe:0}";
var vollData = $"N;;;{area / 10_000.0}";
return $"{_clientData};{memberData};{deliveryData};{vollData}";
}
} }
} }

View File

@ -1,4 +1,61 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public class Csv { public abstract class Csv<T> : IExporter<T> {
public static string FileExtension => "csv";
private readonly StreamWriter _writer;
protected readonly char Separator;
protected string? Header;
public Csv(string filename, char separator = ';') : this(filename, separator, Utils.UTF8) { }
public Csv(string filename, char separator, Encoding encoding) {
_writer = new StreamWriter(filename, false, encoding);
Separator = separator;
}
public void Dispose() {
GC.SuppressFinalize(this);
_writer.Dispose();
}
public ValueTask DisposeAsync() {
GC.SuppressFinalize(this);
return _writer.DisposeAsync();
}
public async Task ExportAsync(IEnumerable<T> data, IProgress<double>? progress = null) {
progress?.Report(0.0);
int count = data.Count() + 2, i = 0;
if (Header != null) await _writer.WriteLineAsync(Header);
progress?.Report(100.0 * ++i / count);
foreach (var row in data) {
await _writer.WriteLineAsync(FormatRow(row));
progress?.Report(100.0 * ++i / count);
}
await _writer.FlushAsync();
progress?.Report(100.0);
}
public void Export(IEnumerable<T> data, IProgress<double>? progress = null) {
ExportAsync(data, progress).GetAwaiter().GetResult();
}
public string FormatRow(IEnumerable row) {
return string.Join(Separator, row);
}
public abstract string FormatRow(T row);
} }
} }

View File

@ -1,15 +1,109 @@
using Elwig.Helpers.Billing;
using Elwig.Models; using Elwig.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public class Ebics : IBankingProvider { public class Ebics : IBankingExporter {
public string FileExtension => "xml"; public static string FileExtension => "xml";
public Task Export(string filename, int avnr, IEnumerable<Member> members) { private readonly StreamWriter _writer;
throw new NotImplementedException(); private readonly DateOnly _date;
private readonly int _year;
private readonly string _name;
private readonly int _nr;
public Ebics(PaymentVar variant, string filename) {
_writer = new(filename, false, Utils.UTF8);
_date = variant.TransferDate ?? DateOnly.Parse("2021-01-10"); //throw new ArgumentException("TransferDate has to be set in PaymentVar");
_year = variant.Year;
_name = variant.Name;
_nr = variant.AvNr;
}
public void Dispose() {
GC.SuppressFinalize(this);
_writer.Dispose();
}
public ValueTask DisposeAsync() {
GC.SuppressFinalize(this);
return _writer.DisposeAsync();
}
public void Export(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
ExportAsync(transactions, progress).GetAwaiter().GetResult();
}
public async Task ExportAsync(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
progress?.Report(0.0);
var nbOfTxs = transactions.Count();
int count = nbOfTxs + 2, i = 0;
var ctrlSum = Transaction.FormatAmountCent(transactions.Sum(tx => tx.AmountCent));
var msgId = $"ELWIG-{App.Client.NameToken}-{_year}-AV{_nr:00}";
var pmtInfId = $"{msgId}-1";
await _writer.WriteLineAsync($"""
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>{msgId}</MsgId>
<CreDtTm>{DateTime.UtcNow:o}</CreDtTm>
<NbOfTxs>{nbOfTxs}</NbOfTxs>
<CtrlSum>{ctrlSum}</CtrlSum>
<InitgPty><Nm>{App.Client.NameFull}</Nm></InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>{pmtInfId}</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<NbOfTxs>{nbOfTxs}</NbOfTxs>
<CtrlSum>{ctrlSum}</CtrlSum>
<ReqdExctnDt><Dt>{_date:yyyy-MM-dd}</Dt></ReqdExctnDt>
<Dbtr><Nm>{App.Client.NameFull}</Nm></Dbtr>
<DbtrAcct><Id><IBAN>{App.Client.Iban?.Replace(" ", "")}</IBAN></Id></DbtrAcct>
<DbtrAgt><FinInstnId><BICFI>{App.Client.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></DbtrAgt>
""");
progress?.Report(100.0 * ++i / count);
foreach (var tx in transactions) {
var a = (IAddress?)tx.Member.BillingAddress ?? tx.Member;
var (a1, a2) = Utils.SplitAddress(a.Address);
var id = $"ELWIG-{App.Client.NameToken}-{_year}-TG{tx.Nr:0000}";
var info = $"{_name} - Traubengutschrift {_year}/{tx.Nr:000}";
await _writer.WriteLineAsync($"""
<CdtTrfTxInf>
<PmtId><EndToEndId>{id}</EndToEndId></PmtId>
<Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmountCent(tx.AmountCent)}</InstdAmt></Amt>
<Cdtr>
<Nm>{a.Name}</Nm>
<PstlAdr>
<StrtNm>{a1}</StrtNm><BldgNb>{a2}</BldgNb>
<PstCd>{a.PostalDest.AtPlz?.Plz}</PstCd><TwnNm>{a.PostalDest.AtPlz?.Ort.Name}</TwnNm>
<Ctry>{a.PostalDest.Country.Alpha2}</Ctry>
</PstlAdr>
</Cdtr>
<CdtrAcct><Id><IBAN>{tx.Member.Iban}</IBAN></Id><CdtrAcct>
<CdtrAgt><FinInstnId><BICFI>{tx.Member.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></CdtrAgt>
<RmtInf><Ustrd>{info}</Ustrd></RmtInf>
</CdtTrfTxInf>
""");
progress?.Report(100.0 * ++i / count);
}
await _writer.WriteLineAsync("""
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
""");
await _writer.FlushAsync();
progress?.Report(100.0);
} }
} }
} }

View File

@ -1,14 +1,14 @@
using Elwig.Models; using Elwig.Helpers.Billing;
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public class Elba : IBankingProvider { public class Elba : Csv<Transaction>, IBankingExporter {
public string FileExtension => "elba"; public static new string FileExtension => "elba";
public Task Export(string filename, int avnr, IEnumerable<Member> members) { public Elba(string filename) : base(filename) { }
public override string FormatRow(Transaction row) {
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }

View File

@ -0,0 +1,8 @@
using Elwig.Helpers.Billing;
namespace Elwig.Helpers.Export {
/// <summary>
/// Interface for exporting banking data
/// </summary>
public interface IBankingExporter : IExporter<Transaction> { }
}

View File

@ -1,24 +0,0 @@
using Elwig.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export {
/// <summary>
/// Interface for exporting banking data
/// </summary>
public interface IBankingProvider {
/// <summary>
/// The default file extension of the exported files to be used (whithout a preceding ".")
/// </summary>
string FileExtension { get; }
/// <summary>
/// Export the member payment data of the given payment variant to the given file.
/// (The amount of the last payed variant is deducted from the calculated amount.)
/// </summary>
/// <param name="filename">The file to export the data to</param>
/// <param name="avnr">The number of the payment variant to export</param>
/// <param name="members">The members whose data to include in the export</param>
Task Export(string filename, int avnr, IEnumerable<Member> members);
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export {
public interface IExporter<T> : IDisposable, IAsyncDisposable {
/// <summary>
/// The default file extension of the exported files to be used (whithout a preceding ".")
/// </summary>
static abstract string FileExtension { get; }
/// <summary>
/// Export the given data to the given file.
/// </summary>
/// <param name="data">The data to be exported</param>
/// <param name="progress">The progress object to report to</param>
void Export(IEnumerable<T> data, IProgress<double>? progress = null);
/// <summary>
/// Asynchronosly export the given data to the given file.
/// </summary>
/// <param name="data">The data to be exported</param>
/// <param name="progress">The progress object to report to</param>
Task ExportAsync(IEnumerable<T> data, IProgress<double>? progress = null);
}
}

View File

@ -0,0 +1,9 @@
using Elwig.Models;
namespace Elwig.Helpers {
public interface IAddress {
string Name { get; }
string Address { get; }
PostalDest PostalDest { get; }
}
}

View File

@ -15,6 +15,8 @@ using Elwig.Models;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static partial class Utils { public static partial class Utils {
public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
public static int CurrentYear => DateTime.Now.Year; public static int CurrentYear => DateTime.Now.Year;
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0); public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 7 ? 1 : 0); public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 7 ? 1 : 0);
@ -25,6 +27,7 @@ namespace Elwig.Helpers {
public static readonly Regex PartialDateRegex = GeneratedPartialDateRegex(); public static readonly Regex PartialDateRegex = GeneratedPartialDateRegex();
public static readonly Regex FromToRegex = GeneratedFromToRegex(); public static readonly Regex FromToRegex = GeneratedFromToRegex();
public static readonly Regex FromToTimeRegex = GeneratedFromToTimeRegex(); public static readonly Regex FromToTimeRegex = GeneratedFromToTimeRegex();
public static readonly Regex AddressRegex = GeneratedAddressRegex();
[GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)] [GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)]
private static partial Regex GeneratedSerialRegex(); private static partial Regex GeneratedSerialRegex();
@ -41,6 +44,9 @@ namespace Elwig.Helpers {
[GeneratedRegex(@"^([0-9]{1,2}:[0-9]{2})?-([0-9]{1,2}:[0-9]{2})?$", RegexOptions.Compiled)] [GeneratedRegex(@"^([0-9]{1,2}:[0-9]{2})?-([0-9]{1,2}:[0-9]{2})?$", RegexOptions.Compiled)]
private static partial Regex GeneratedFromToTimeRegex(); private static partial Regex GeneratedFromToTimeRegex();
[GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)]
private static partial Regex GeneratedAddressRegex();
private static readonly ushort[] Crc16ModbusTable = { private static readonly ushort[] Crc16ModbusTable = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
@ -213,8 +219,8 @@ namespace Elwig.Helpers {
.Sum(); .Sum();
} }
public static (int, string?)? ShowManualWeighingDialog() { public static (int, string?)? ShowManualWeighingDialog(string? reason = null) {
var d = new ManualWeighingDialog(); var d = new ManualWeighingDialog(reason);
return d.ShowDialog() == true ? (d.Weight, d.Reason) : null; return d.ShowDialog() == true ? (d.Weight, d.Reason) : null;
} }
@ -292,5 +298,30 @@ namespace Elwig.Helpers {
public static string GenerateLsNr(Delivery d) => GenerateLsNr(d.Date, d.ZwstId, d.LNr); public static string GenerateLsNr(Delivery d) => GenerateLsNr(d.Date, d.ZwstId, d.LNr);
public static string GenerateLsNr(DateOnly date, string zwstid, int lnr) => $"{date:yyyyMMdd}{zwstid}{lnr:000}"; public static string GenerateLsNr(DateOnly date, string zwstid, int lnr) => $"{date:yyyyMMdd}{zwstid}{lnr:000}";
public static (string, string?) SplitAddress(string address) {
var m = AddressRegex.Match(address);
return (m.Groups[1].Value, m.Groups[2].Value);
}
public static (string, string?) SplitName(string fullName, string? familyName) {
if (familyName == null || familyName == "") return (fullName, null);
var p0 = fullName.ToLower().IndexOf(familyName.ToLower());
if (p0 == -1) return (fullName, null);
var p1 = fullName.IndexOf(" und ");
var p2 = fullName.ToLower().LastIndexOf(" und ");
if (p1 != p2) {
if (p0 > p1) {
// A und B familyName [und ...]
return (fullName[p0..^0], fullName[0..(p0 - 1)]);
} else {
// familyName und ... A und B
var p3 = fullName.LastIndexOf(' ', p2 - 1);
return (fullName[0..p3], fullName[(p3 + 1)..^0]);
}
} else {
return (familyName, fullName.Replace(familyName, "").Replace(" ", " ").Trim());
}
}
} }
} }

View File

@ -0,0 +1,44 @@
using System;
using System.Threading.Tasks;
namespace Elwig.Helpers.Weighing {
public class InvalidScale : IScale {
public string Manufacturer => "NONE";
public string Model => "NONE";
public string ScaleId { get; private set; }
public int InternalScaleNr => 0;
public bool IsReady => false;
public bool HasFillingClearance => false;
public int? WeightLimit => null;
public string? LogPath => null;
public InvalidScale(string id) {
ScaleId = id;
}
public void Dispose() {
GC.SuppressFinalize(this);
}
public Task<WeighingResult> Weigh() {
throw new NotImplementedException();
}
public Task<WeighingResult> GetCurrentWeight() {
throw new NotImplementedException();
}
public Task Empty() {
throw new NotImplementedException();
}
public Task GrantFillingClearance() {
throw new NotImplementedException();
}
public Task RevokeFillingClearance() {
throw new NotImplementedException();
}
}
}

View File

@ -1,9 +1,10 @@
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models { namespace Elwig.Models {
[Table("member_billing_address"), PrimaryKey("MgNr")] [Table("member_billing_address"), PrimaryKey("MgNr")]
public class BillingAddr { public class BillingAddr : IAddress {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }

104
Elwig/Models/Credit.cs Normal file
View File

@ -0,0 +1,104 @@
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("credit"), PrimaryKey("Year", "TgNr"), Index("Year", "AvNr", "MgNr", IsUnique = true)]
public class Credit {
[Column("year")]
public int Year { get; set; }
[Column("tgnr")]
public int TgNr { get; set; }
[NotMapped]
public string TgId => $"{Year}/{TgNr:000}";
[Column("mgnr")]
public int MgNr { get; set; }
[Column("avnr")]
public int AvNr { get; set; }
[Column("net_amount")]
public long NetAmountValue { get; set; }
[NotMapped]
public decimal NetAmount {
get => Utils.DecFromDb(NetAmountValue, 2);
set => NetAmountValue = Utils.DecToDb(value, 2);
}
[Column("prev_net_amount")]
public long? PrevNetAmountValue { get; set; }
[NotMapped]
public decimal? PrevNetAmount {
get => PrevNetAmountValue != null ? Utils.DecFromDb(PrevNetAmountValue.Value, 2) : null;
set => PrevNetAmountValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
}
[Column("vat")]
public double VatValue { get; set; }
[NotMapped]
public decimal Vat {
get => (decimal)VatValue;
set => VatValue = (double)value;
}
[Column("vat_amount")]
public long VatAmountValue { get; private set; }
[NotMapped]
public decimal VatAmount {
get => Utils.DecFromDb(VatAmountValue, 2);
}
[Column("gross_amount")]
public long GrossAmountValue { get; private set; }
[NotMapped]
public decimal GrossAmount {
get => Utils.DecFromDb(GrossAmountValue, 2);
}
[Column("modifiers")]
public long? ModifiersValue { get; set; }
[NotMapped]
public decimal? Modifiers {
get => ModifiersValue != null ? Utils.DecFromDb(ModifiersValue.Value, 2) : null;
set => ModifiersValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
}
[Column("prev_modifiers")]
public long? PrevModifiersValue { get; set; }
[NotMapped]
public decimal? PrevModifiers {
get => PrevModifiersValue != null ? Utils.DecFromDb(PrevModifiersValue.Value, 2) : null;
set => PrevModifiersValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
}
[Column("amount")]
public long AmountValue { get; private set; }
[NotMapped]
public decimal Amount {
get => Utils.DecFromDb(AmountValue, 2);
}
[Column("ctime")]
public long CTime { get; private set; }
[NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[Column("mtime")]
public long MTime { get; private set; }
[NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[ForeignKey("Year, AvNr, MgNr")]
public virtual PaymentMember Payment { get; private set; }
[ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; }
}
}

View File

@ -19,12 +19,8 @@ namespace Elwig.Models {
[NotMapped] [NotMapped]
public DateOnly Date { public DateOnly Date {
get { get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
return DateOnly.ParseExact(DateString, "yyyy-MM-dd"); set => DateString = value.ToString("yyyy-MM-dd");
}
set {
DateString = value.ToString("yyyy-MM-dd");
}
} }
[Column("time")] [Column("time")]
@ -32,19 +28,13 @@ namespace Elwig.Models {
[NotMapped] [NotMapped]
public TimeOnly? Time { public TimeOnly? Time {
get { get => (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss");
return (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss"); set => TimeString = value?.ToString("HH:mm:ss");
}
set {
TimeString = value?.ToString("HH:mm:ss");
}
} }
[NotMapped] [NotMapped]
public DateTime DateTime { public DateTime DateTime {
get { get => Date.ToDateTime(Time ?? TimeOnly.MinValue);
return Date.ToDateTime(Time ?? TimeOnly.MinValue);
}
set { set {
Date = DateOnly.FromDateTime(value); Date = DateOnly.FromDateTime(value);
Time = TimeOnly.FromDateTime(value); Time = TimeOnly.FromDateTime(value);
@ -72,6 +62,9 @@ namespace Elwig.Models {
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[ForeignKey("Year")]
public virtual Season Season { get; private set; }
[InverseProperty("Delivery")] [InverseProperty("Delivery")]
public virtual ISet<DeliveryPart> Parts { get; private set; } public virtual ISet<DeliveryPart> Parts { get; private set; }

View File

@ -30,15 +30,10 @@ namespace Elwig.Models {
[Column("kmw")] [Column("kmw")]
public double Kmw { get; set; } public double Kmw { get; set; }
[NotMapped] [NotMapped]
public double Oe { public double Oe {
get { get => Utils.KmwToOe(Kmw);
return Utils.KmwToOe(Kmw); set => Kmw = Utils.OeToKmw(value);
}
set {
Kmw = Utils.OeToKmw(value);
}
} }
[Column("qualid")] [Column("qualid")]
@ -92,6 +87,9 @@ namespace Elwig.Models {
[Column("weighing_id")] [Column("weighing_id")]
public string? WeighingId { get; set; } public string? WeighingId { get; set; }
[Column("weighing_reason")]
public string? WeighingReason { get; set; }
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
@ -107,6 +105,10 @@ namespace Elwig.Models {
[NotMapped] [NotMapped]
public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering); public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering);
[InverseProperty("DeliveryPart")]
public virtual PaymentDeliveryPart? Payment { get; private set; }
[NotMapped]
public string OriginString => Origin.OriginString + "\n" + (Kg?.Gl != null ? $" / {Kg.Gl.Name}" : "") + (Kg != null ? $" / {Kg.AtKg.Gem.Name} / KG {Kg.AtKg.Name}" : "") + (Rd != null ? $" / Ried {Rd.Name}" : ""); public string OriginString => Origin.OriginString + "\n" + (Kg?.Gl != null ? $" / {Kg.Gl.Name}" : "") + (Kg != null ? $" / {Kg.AtKg.Gem.Name} / KG {Kg.AtKg.Name}" : "") + (Rd != null ? $" / Ried {Rd.Name}" : "");
} }
} }

View File

@ -7,7 +7,7 @@ using System.Linq;
namespace Elwig.Models { namespace Elwig.Models {
[Table("member"), PrimaryKey("MgNr")] [Table("member"), PrimaryKey("MgNr")]
public class Member { public class Member : IAddress {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
@ -128,9 +128,6 @@ namespace Elwig.Models {
[Column("address")] [Column("address")]
public string Address { get; set; } public string Address { get; set; }
[Column("email")]
public string? Email { get; set; }
[Column("default_kgnr")] [Column("default_kgnr")]
public int? DefaultKgNr { get; set; } public int? DefaultKgNr { get; set; }
@ -174,6 +171,9 @@ namespace Elwig.Models {
[InverseProperty("Member")] [InverseProperty("Member")]
public virtual ISet<MemberTelNr> TelephoneNumbers { get; private set; } public virtual ISet<MemberTelNr> TelephoneNumbers { get; private set; }
[InverseProperty("member")]
public virtual ISet<MemberEmailAddr> EmailAddresses { get; private set; }
public string FullAddress => $"{Address}, {PostalDest.AtPlz.Plz} {PostalDest.AtPlz.Ort.Name}"; public string FullAddress => $"{Address}, {PostalDest.AtPlz.Plz} {PostalDest.AtPlz.Ort.Name}";
public int DeliveryRight => BusinessShares * App.Client.DeliveryRight; public int DeliveryRight => BusinessShares * App.Client.DeliveryRight;

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("member_email_address"), PrimaryKey("MgNr", "Nr")]
public class MemberEmailAddr {
[Column("mgnr")]
public int MgNr { get; set; }
[Column("nr")]
public int Nr { get; set; }
[Column("address")]
public string Address { get; set; }
[Column("comment")]
public string? Comment { get; set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; }
}
}

View File

@ -0,0 +1,158 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace Elwig.Models {
[Table("payment_delivery_part"), PrimaryKey("Year", "DId", "DPNr", "AvNr")]
public class PaymentDeliveryPart {
[Column("year")]
public int Year { get; set; }
[Column("did")]
public int DId { get; set; }
[Column("dpnr")]
public int DPNr { get; set; }
[Column("avnr")]
public int AvNr { get; set; }
[Column("mod_abs")]
public long ModAbsValue { get; set; }
[NotMapped]
public decimal ModAbs {
get => Variant.Season.DecFromDb(ModAbsValue);
set => ModAbsValue = Variant.Season.DecToDb(value);
}
[Column("mod_rel")]
public double ModRelValue { get; set; }
[NotMapped]
public decimal ModRel {
get => (decimal)ModRelValue;
set => ModRelValue = (double)value;
}
[Column("bucket_1")]
public int? Bucket1 { get; set; }
[Column("bucket_2")]
public int? Bucket2 { get; set; }
[Column("bucket_3")]
public int? Bucket3 { get; set; }
[Column("bucket_4")]
public int? Bucket4 { get; set; }
[Column("bucket_5")]
public int? Bucket5 { get; set; }
[Column("bucket_6")]
public int? Bucket6 { get; set; }
[Column("bucket_7")]
public int? Bucket7 { get; set; }
[Column("bucket_8")]
public int? Bucket8 { get; set; }
[Column("bucket_9")]
public int? Bucket9 { get; set; }
[NotMapped]
public int[] Buckets => (new int?[] { Bucket1, Bucket2, Bucket3, Bucket4, Bucket5, Bucket6, Bucket7, Bucket8, Bucket9 })
.Where(b => b != null).Select(b => b.Value).ToArray();
[Column("price_1")]
public long? Price1Value { get; set; }
[NotMapped]
public decimal? Price1 {
get => Price1Value != null ? Variant.Season.DecFromDb(Price1Value.Value) : null;
set => Price1Value = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[Column("price_2")]
public long? Price2Value { get; set; }
[NotMapped]
public decimal? Price2 {
get => Price2Value != null ? Variant.Season.DecFromDb(Price2Value.Value) : null;
set => Price2Value = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[Column("price_3")]
public long? Price3Value { get; set; }
[NotMapped]
public decimal? Price3 {
get => Price3Value != null ? Variant.Season.DecFromDb(Price3Value.Value) : null;
set => Price3Value = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[Column("price_4")]
public long? Price4Value { get; set; }
[NotMapped]
public decimal? Price4 {
get => Price4Value != null ? Variant.Season.DecFromDb(Price4Value.Value) : null;
set => Price4Value = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[Column("price_5")]
public long? Price5Value { get; set; }
[NotMapped]
public decimal? Price5 {
get => Price5Value != null ? Variant.Season.DecFromDb(Price5Value.Value) : null;
set => Price5Value = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[Column("price_6")]
public long? Price6Value { get; set; }
[NotMapped]
public decimal? Price6 {
get => Price6Value != null ? Variant.Season.DecFromDb(Price6Value.Value) : null;
set => Price6Value = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[Column("price_7")]
public long? Price7Value { get; set; }
[NotMapped]
public decimal? Price7 {
get => Price7Value != null ? Variant.Season.DecFromDb(Price7Value.Value) : null;
set => Price7Value = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[Column("price_8")]
public long? Price8Value { get; set; }
[NotMapped]
public decimal? Price8 {
get => Price8Value != null ? Variant.Season.DecFromDb(Price8Value.Value) : null;
set => Price8Value = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[Column("price_9")]
public long? Price9Value { get; set; }
[NotMapped]
public decimal? Price9 {
get => Price9Value != null ? Variant.Season.DecFromDb(Price9Value.Value) : null;
set => Price9Value = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[Column("amount")]
public long? AmountValue { get; set; }
[NotMapped]
public decimal? Amount {
get => AmountValue != null ? Variant.Season.DecFromDb(AmountValue.Value) : null;
set => AmountValue = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[NotMapped]
public decimal[] Prices => (new decimal?[] { Price1, Price2, Price3, Price4, Price5, Price6, Price7, Price8, Price9 })
.Where(p => p != null).Select(p => p.Value).ToArray();
[ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; }
[ForeignKey("Year, DId, DPNr")]
public virtual DeliveryPart DeliveryPart { get; private set; }
}
}

View File

@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("payment_member"), PrimaryKey("Year", "AvNr", "MgNr")]
public class PaymentMember {
[Column("year")]
public int Year { get; set; }
[Column("avnr")]
public int AvNr { get; set; }
[Column("mgnr")]
public int MgNr { get; set; }
[Column("amount")]
public long AmountValue { get; set; }
[NotMapped]
public decimal Amount {
get => Variant.Season.DecFromDb(AmountValue);
set => AmountValue = Variant.Season.DecToDb(value);
}
[ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; }
[InverseProperty("Payment")]
public virtual Credit? Credit { get; private set; }
}
}

View File

@ -1,6 +1,8 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace Elwig.Models { namespace Elwig.Models {
[Table("payment_variant"), PrimaryKey("Year", "AvNr")] [Table("payment_variant"), PrimaryKey("Year", "AvNr")]
@ -19,12 +21,17 @@ namespace Elwig.Models {
[NotMapped] [NotMapped]
public DateOnly Date { public DateOnly Date {
get { get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
return DateOnly.ParseExact(DateString, "yyyy-MM-dd"); set => DateString = value.ToString("yyyy-MM-dd");
} }
set {
DateString = value.ToString("yyyy-MM-dd"); [Column("transfer_date")]
} public string? TransferDateString { get; set; }
[NotMapped]
public DateOnly? TransferDate {
get => TransferDateString != null ? DateOnly.ParseExact(TransferDateString, "yyyy-MM-dd") : null;
set => TransferDateString = value?.ToString("yyyy-MM-dd");
} }
[Column("test_variant")] [Column("test_variant")]
@ -60,10 +67,23 @@ namespace Elwig.Models {
[Column("bucket_9_name")] [Column("bucket_9_name")]
public string? Bucket9Name { get; set; } public string? Bucket9Name { get; set; }
[NotMapped]
public string[] BucketNames => (new string?[] { Bucket1Name, Bucket2Name, Bucket3Name, Bucket4Name, Bucket5Name, Bucket6Name, Bucket7Name, Bucket8Name, Bucket9Name })
.Where(n => n != null).Select(n => n ?? "").ToArray();
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[Column("data")] [Column("data")]
public string Data { get; set; } public string Data { get; set; }
[ForeignKey("Year")]
public virtual Season Season { get; private set; }
[InverseProperty("Variant")]
public virtual ISet<PaymentMember> MemberPayments { get; private set; }
[InverseProperty("Variant")]
public virtual ISet<Credit> Credits { get; private set; }
} }
} }

View File

@ -48,6 +48,12 @@ namespace Elwig.Models {
[InverseProperty("Season")] [InverseProperty("Season")]
public virtual ISet<Modifier> Modifiers { get; private set; } public virtual ISet<Modifier> Modifiers { get; private set; }
[InverseProperty("Season")]
public virtual ISet<PaymentVar> PaymentVariants { get; private set; }
[InverseProperty("Season")]
public virtual ISet<Delivery> Deliveries { get; private set; }
public decimal DecFromDb(long value) { public decimal DecFromDb(long value) {
return Utils.DecFromDb(value, Precision); return Utils.DecFromDb(value, Precision);
} }

View File

@ -18,6 +18,9 @@ namespace Elwig.Models {
public string CommentFormat => (Comment != null) ? $" ({Comment})" : ""; public string CommentFormat => (Comment != null) ? $" ({Comment})" : "";
public bool IsRed => Type == "R";
public bool IsWhite => Type == "W";
public override string ToString() { public override string ToString() {
return Name; return Name;
} }

View File

@ -32,6 +32,7 @@ namespace Elwig.Windows {
LockContext = IsEditing; LockContext = IsEditing;
} }
} }
protected bool DoShowWarningWindows = true;
protected bool IsClosing { get; private set; } protected bool IsClosing { get; private set; }
private TextBox[] TextBoxInputs; private TextBox[] TextBoxInputs;
@ -81,6 +82,14 @@ namespace Elwig.Windows {
} }
private void OnClosing(object? sender, CancelEventArgs evt) { private void OnClosing(object? sender, CancelEventArgs evt) {
if ((IsCreating || IsEditing) && HasChanged) {
var r = System.Windows.MessageBox.Show("Soll das Fenster wirklich geschlossen werden?", "Schlie<69>en best<73>tigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) {
evt.Cancel = true;
return;
}
}
IsClosing = true; IsClosing = true;
} }
@ -292,12 +301,20 @@ namespace Elwig.Windows {
} }
protected bool HasChanged => protected bool HasChanged =>
!IsValid || IsEditing && (
TextBoxInputs.Any(InputHasChanged) || !IsValid ||
ComboBoxInputs.Any(InputHasChanged) || TextBoxInputs.Any(InputHasChanged) ||
CheckComboBoxInputs.Any(InputHasChanged) || ComboBoxInputs.Any(InputHasChanged) ||
CheckBoxInputs.Any(InputHasChanged) || CheckComboBoxInputs.Any(InputHasChanged) ||
RadioButtonInputs.Any(InputHasChanged); CheckBoxInputs.Any(InputHasChanged) ||
RadioButtonInputs.Any(InputHasChanged)
) || IsCreating && (
TextBoxInputs.Any(i => InputIsNotDefault(i) || (!i.IsReadOnly && i.Text != "")) ||
ComboBoxInputs.Any(i => InputIsNotDefault(i) || (i.IsEnabled && i.SelectedItem != null)) ||
CheckComboBoxInputs.Any(i => InputIsNotDefault(i) || i.SelectedItem != null) ||
CheckBoxInputs.Any(InputIsNotDefault) ||
RadioButtonInputs.Any(InputIsNotDefault)
);
protected void UpdatePlz(TextBox plzInput, ComboBox ortInput) { protected void UpdatePlz(TextBox plzInput, ComboBox ortInput) {
var plzInputValid = GetInputValid(plzInput); var plzInputValid = GetInputValid(plzInput);
@ -363,7 +380,7 @@ namespace Elwig.Windows {
} }
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) { protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
if (!res.IsValid && !IsClosing && (IsEditing || IsCreating)) if (DoShowWarningWindows && !res.IsValid && !IsClosing && (IsEditing || IsCreating))
System.Windows.MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning); System.Windows.MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
return res.IsValid; return res.IsValid;
} }
@ -396,7 +413,7 @@ namespace Elwig.Windows {
UpdateButtons(); UpdateButtons();
} }
protected void TextBox_TextChanged(object sender, RoutedEventArgs evt) { protected void TextBox_TextChanged(object sender, RoutedEventArgs? evt) {
var input = (TextBox)sender; var input = (TextBox)sender;
if (SenderIsRequired(input) && input.Text.Length == 0) { if (SenderIsRequired(input) && input.Text.Length == 0) {
ValidateInput(input, false); ValidateInput(input, false);

View File

@ -6,7 +6,7 @@
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d" mc:Ignorable="d"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="Flächenbindugen - Elwig" Height="480" Width="850" Title="Flächenbindungen - Elwig" Height="480" Width="850"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
@ -131,6 +131,10 @@
<Label Content="Bewirt.-Art:" Margin="10,100,0,0" Grid.Column="0" Grid.ColumnSpan="2"/> <Label Content="Bewirt.-Art:" Margin="10,100,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
<ComboBox x:Name="WineCultivationInput" DisplayMemberPath="Name" TextSearch.TextPath="Name" <ComboBox x:Name="WineCultivationInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
HorizontalAlignment="Stretch" Margin="0,100,10,0" Grid.Column="1" Grid.ColumnSpan="3"/> HorizontalAlignment="Stretch" Margin="0,100,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
<Label Content="Anmerkung:" Margin="10,130,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
<TextBox x:Name="CommentInput" TextChanged="TextBox_TextChanged"
HorizontalAlignment="Stretch" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
</Grid> </Grid>
</GroupBox> </GroupBox>

View File

@ -40,11 +40,8 @@ namespace Elwig.Windows {
private async Task RefreshAreaCommitmentListQuery() { private async Task RefreshAreaCommitmentListQuery() {
List<AreaCom> areaComs = await Context.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr).ToListAsync(); List<AreaCom> areaComs = await Context.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr).ToListAsync();
ControlUtils.RenewItemsSource(AreaCommitmentList, areaComs, i => (i as AreaCom)?.FbNr,
ControlUtils.RenewItemsSource(AreaCommitmentList, areaComs, i => (i as AreaCom)?.FbNr); AreaCommitmentList_SelectionChanged, ControlUtils.RenewSourceDefault.None);
if (areaComs.Count == 1)
AreaCommitmentList.SelectedIndex = 0;
RefreshInputs(); RefreshInputs();
} }
@ -82,6 +79,8 @@ namespace Elwig.Windows {
AreaComTypeInput.SelectedItem = a.AreaComType; AreaComTypeInput.SelectedItem = a.AreaComType;
WineCultivationInput.SelectedItem = a.WineCult; WineCultivationInput.SelectedItem = a.WineCult;
CommentInput.Text = a.Comment;
FinishInputFilling(); FinishInputFilling();
} }
@ -151,6 +150,7 @@ namespace Elwig.Windows {
a.Area = int.Parse(AreaInput.Text); a.Area = int.Parse(AreaInput.Text);
a.VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)?.VtrgId; a.VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)?.VtrgId;
a.CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId; a.CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId;
a.Comment = (CommentInput.Text == "") ? null : CommentInput.Text;
EntityEntry<AreaCom>? tr = null; EntityEntry<AreaCom>? tr = null;
try { try {

View File

@ -212,7 +212,7 @@ namespace Elwig.Windows {
p.Address = ClientAddressInput.Text; p.Address = ClientAddressInput.Text;
p.Plz = int.Parse(ClientPlzInput.Text); p.Plz = int.Parse(ClientPlzInput.Text);
p.Ort = ClientOrtInput.Text; p.Ort = ClientOrtInput.Text;
p.Iban = ClientIbanInput.Text.Length > 0 ? ClientIbanInput.Text.Replace(" ", "") : null; p.Iban = ClientIbanInput.Text.Length > 0 ? ClientIbanInput.Text : null;
p.Bic = ClientBicInput.Text.Length > 0 ? ClientBicInput.Text : null; p.Bic = ClientBicInput.Text.Length > 0 ? ClientBicInput.Text : null;
p.UstIdNr = ClientUstIdNrInput.Text.Length > 0 ? ClientUstIdNrInput.Text : null; p.UstIdNr = ClientUstIdNrInput.Text.Length > 0 ? ClientUstIdNrInput.Text : null;
p.LfbisNr = ClientLfbisNrInput.Text.Length > 0 ? ClientLfbisNrInput.Text : null; p.LfbisNr = ClientLfbisNrInput.Text.Length > 0 ? ClientLfbisNrInput.Text : null;

View File

@ -1,21 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models; using Elwig.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using Newtonsoft.Json.Linq;
using ScottPlot; using ScottPlot;
using ScottPlot.Plottable; using ScottPlot.Plottable;
@ -69,14 +65,14 @@ namespace Elwig.Windows {
} }
PaymentVar paymentVar = paymentVars[0]; PaymentVar paymentVar = paymentVars[0];
var data = JToken.Parse(paymentVar.Data); var data = JsonNode.Parse(paymentVar.Data).AsObject();
var auszahlungsSorten = data["AuszahlungSorten"]; var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
if (auszahlungsSorten == null) { if (auszahlungsSorten == null) {
return; return;
} }
var Graphs = auszahlungsSorten["Kurven"]; var Graphs = auszahlungsSorten["Kurven"]?.AsArray();
if (Graphs == null) { if (Graphs == null) {
return; return;
@ -86,7 +82,7 @@ namespace Elwig.Windows {
int i = 1; int i = 1;
foreach (var graph in Graphs) { foreach (var graph in Graphs) {
GraphsList.Add(new Graph("Oe", i, graph, ParseContracts(auszahlungsSorten, i - 1), 50, 140)); GraphsList.Add(new Graph("Oe", i, graph?.AsObject(), ParseContracts(auszahlungsSorten, i - 1), 50, 140));
i++; i++;
} }
@ -98,17 +94,15 @@ namespace Elwig.Windows {
RefreshInputs(); RefreshInputs();
} }
private String ParseContracts(JToken auszahlungsSorten, int num) { private String ParseContracts(JsonObject auszahlungsSorten, int num) {
List<string> contracts = new(); List<string> contracts = new();
foreach (var sorte in auszahlungsSorten.Children().OfType<JToken>()) { foreach (var sorte in auszahlungsSorten) {
if (((JProperty)sorte).Name == "Kurven") { if (sorte.Key == "Kurven") continue;
continue; foreach (var attribut in sorte.Value.AsObject()) {
} foreach (var bindung in attribut.Value.AsObject()) {
foreach (var attribut in sorte.Values().OfType<JToken>()) { if ((int)bindung.Value.AsValue() == num) {
foreach (var bindung in attribut.Values().OfType<JProperty>()) { contracts.Add($"{sorte.Key}/{attribut.Key}/{bindung.Key}");
if ((int)(bindung).Value == num) {
contracts.Add($"{((JProperty)sorte).Name}/{((JProperty)attribut).Name}/{bindung.Name}");
} }
} }
} }
@ -125,15 +119,14 @@ namespace Elwig.Windows {
} }
PaymentVar paymentVar = paymentVars[0]; PaymentVar paymentVar = paymentVars[0];
var data = JToken.Parse(paymentVar.Data); var data = JsonNode.Parse(paymentVar.Data).AsObject();
var auszahlungsSorten = data["AuszahlungSorten"]; var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
if (auszahlungsSorten == null) { if (auszahlungsSorten == null) {
return false; return false;
} }
var Graphs = auszahlungsSorten["Kurven"]; var Graphs = auszahlungsSorten["Kurven"]?.AsObject();
if (Graphs == null) { if (Graphs == null) {
return false; return false;
} }
@ -141,26 +134,24 @@ namespace Elwig.Windows {
int i = 1; int i = 1;
foreach (var graph in Graphs) { foreach (var graph in Graphs) {
if (i == num) { if (i == num) {
graph.Remove(); Graphs.Remove(graph.Key);
break; break;
} }
i++; i++;
} }
foreach (var sorte in auszahlungsSorten.Children().OfType<JToken>()) { foreach (var sorte in auszahlungsSorten) {
if (((JProperty)sorte).Name == "Kurven") { if (sorte.Key == "Kurven") continue;
continue; foreach (var attribut in sorte.Value.AsObject()) {
} var bindungen = attribut.Value.AsObject();
foreach (var attribut in sorte.Values().OfType<JToken>()) { foreach (var bindung in bindungen) {
List<JProperty> itemsToRemove = new(); int v = (int)bindung.Value;
foreach (var bindung in attribut.Values().OfType<JProperty>()) { if (v == num - 1) {
if ((int)bindung.Value == num - 1) { bindungen.Remove(bindung.Key);
itemsToRemove.Add(bindung); } else if (v > num - 1) {
} else if ((int)bindung.Value > num - 1) { bindungen[bindung.Key] = v - 1;
bindung.Value = (int)bindung.Value - 1;
} }
} }
itemsToRemove.ForEach(i => i.Remove());
} }
} }
@ -231,6 +222,7 @@ namespace Elwig.Windows {
private void InitPlot() { private void InitPlot() {
OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(Graph.DataX, Graph.DataY); OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(Graph.DataX, Graph.DataY);
OechslePricePlot.Configuration.DoubleClickBenchmark = false;
OechslePricePlotScatter.LineColor = Color.Blue; OechslePricePlotScatter.LineColor = Color.Blue;
OechslePricePlotScatter.MarkerColor = Color.Blue; OechslePricePlotScatter.MarkerColor = Color.Blue;
OechslePricePlotScatter.MarkerSize = 9; OechslePricePlotScatter.MarkerSize = 9;
@ -398,7 +390,7 @@ namespace Elwig.Windows {
PriceInput.Text = Graph.DataY[PrimaryMarkedPointIndex].ToString(); PriceInput.Text = Graph.DataY[PrimaryMarkedPointIndex].ToString();
EnableActionButtons(); if (IsEditing || IsCreating) EnableActionButtons();
OechslePricePlot.Render(); OechslePricePlot.Render();
return; return;
} }
@ -430,6 +422,8 @@ namespace Elwig.Windows {
return; return;
} }
FlattenGraph(0, PrimaryMarkedPointIndex, Graph.DataY[PrimaryMarkedPointIndex]); FlattenGraph(0, PrimaryMarkedPointIndex, Graph.DataY[PrimaryMarkedPointIndex]);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void RightFlatButton_Click(object sender, RoutedEventArgs evt) { private void RightFlatButton_Click(object sender, RoutedEventArgs evt) {
@ -437,6 +431,8 @@ namespace Elwig.Windows {
return; return;
} }
FlattenGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, Graph.DataY[PrimaryMarkedPointIndex]); FlattenGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, Graph.DataY[PrimaryMarkedPointIndex]);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void InterpolateButton_Click(object sender, RoutedEventArgs evt) { private void InterpolateButton_Click(object sender, RoutedEventArgs evt) {
@ -451,6 +447,8 @@ namespace Elwig.Windows {
for (int i = lowIndex; i < highIndex - 1; i++) { for (int i = lowIndex; i < highIndex - 1; i++) {
Graph.DataY[i + 1] = Math.Round(Graph.DataY[i] + step, 4); // TODO richtig runden Graph.DataY[i + 1] = Math.Round(Graph.DataY[i] + step, 4); // TODO richtig runden
} }
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) { private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) {
@ -462,6 +460,8 @@ namespace Elwig.Windows {
return; return;
} }
LinearIncreaseGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, priceIncrease.Value); LinearIncreaseGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, priceIncrease.Value);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) { private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) {
@ -662,23 +662,22 @@ namespace Elwig.Windows {
} }
PaymentVar paymentVar = paymentVars[0]; PaymentVar paymentVar = paymentVars[0];
var data = JToken.Parse(paymentVar.Data); var data = JsonNode.Parse(paymentVar.Data).AsObject();
var auszahlungsSorten = data["AuszahlungSorten"]; var auszahlungsSorten = data["AuszahlungSorten"];
if (auszahlungsSorten == null) { if (auszahlungsSorten == null) {
return null; return null;
} }
var Graphs = auszahlungsSorten["Kurven"]; var Graphs = auszahlungsSorten["Kurven"].AsArray();
if (Graphs == null) { if (Graphs == null) {
return null; return null;
} }
if (IsEditing) { if (IsEditing) {
((JArray)Graphs)[g.Num - 1] = g.ToJson(); Graphs[g.Num - 1] = g.ToJson();
} else if(IsCreating) { } else if(IsCreating) {
((JArray)Graphs).Add(g.ToJson()); Graphs.Add(g.ToJson());
} else { } else {
return null; return null;
} }

View File

@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="Lieferungsverwaltung - Elwig" Height="720" Width="1100" MinHeight="700" MinWidth="1000" Title="Lieferungen - Elwig" Height="720" Width="1100" MinHeight="700" MinWidth="1000"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
@ -65,8 +65,7 @@
Click="Menu_Print_PrintDeliveryNote_Click"/> Click="Menu_Print_PrintDeliveryNote_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="Exportieren"> <MenuItem Header="Exportieren">
<MenuItem x:Name="Menu_Export_Bki" Header="Traubentransportscheinliste (BKI)" <MenuItem x:Name="Menu_Export_Bki" Header="Traubentransportscheinliste (BKI)"/>
Click="Menu_Export_Bki_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="Werkzeuge"> <MenuItem Header="Werkzeuge">
<MenuItem Header="Alle Lieferscheine überprüfen"/> <MenuItem Header="Alle Lieferscheine überprüfen"/>
@ -161,10 +160,10 @@
<Label Content="Mitglied:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="Mitglied:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="MgNrInput" Width="48" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" TextAlignment="Right" <TextBox x:Name="MgNrInput" Width="48" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus"/> TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus" KeyUp="Input_KeyUp"/>
<ComboBox x:Name="MemberInput" Grid.Column="1" Margin="53,10,10,10" IsEditable="True" <ComboBox x:Name="MemberInput" Grid.Column="1" Margin="53,10,10,10" IsEditable="True"
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName" ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
SelectionChanged="MemberInput_SelectionChanged"/> SelectionChanged="MemberInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<Label Content="Wohnort:" Margin="10,38,0,0" Grid.Column="0"/> <Label Content="Wohnort:" Margin="10,38,0,0" Grid.Column="0"/>
<TextBox x:Name="MemberAddressField" Grid.Column="1" Margin="0,40,10,10" FontSize="12" Height="22" <TextBox x:Name="MemberAddressField" Grid.Column="1" Margin="0,40,10,10" FontSize="12" Height="22"
@ -210,15 +209,15 @@
<Label Content="Sorte:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="Sorte:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="SortIdInput" Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" <TextBox x:Name="SortIdInput" Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left"
TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus"/> TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus" KeyUp="Input_KeyUp"/>
<ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Margin="41,10,10,10" <ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Margin="41,10,10,10"
ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name" ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name"
SelectionChanged="WineVarietyInput_SelectionChanged"/> SelectionChanged="WineVarietyInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<Label Content="Attribute:" Margin="10,40,0,0" Grid.Column="0"/> <Label Content="Attribute:" Margin="10,40,0,0" Grid.Column="0"/>
<xctk:CheckComboBox x:Name="AttributesInput" Grid.Row="1" Grid.Column="1" Margin="0,40,10,10" <xctk:CheckComboBox x:Name="AttributesInput" Grid.Row="1" Grid.Column="1" Margin="0,40,10,10"
DisplayMemberPath="Name" Delimiter=", " AllItemsSelectedContent="Alle" DisplayMemberPath="Name" Delimiter=", " AllItemsSelectedContent="Alle"
ItemSelectionChanged="AttributesInput_SelectionChanged"/> ItemSelectionChanged="AttributesInput_SelectionChanged" KeyUp="Input_KeyUp"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
@ -247,7 +246,7 @@
SelectionChanged="WineQualityLevelInput_SelectionChanged"/> SelectionChanged="WineQualityLevelInput_SelectionChanged"/>
<CheckBox x:Name="AbgewertetInput" Content="Abgewertet" IsEnabled="False" <CheckBox x:Name="AbgewertetInput" Content="Abgewertet" IsEnabled="False"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,75,10,10" Grid.Column="1"/> VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
@ -273,22 +272,21 @@
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/> Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<Button x:Name="WeighingManualButton" Content="Handwiegung" Width="120"
Click="WeighingManualButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,10,10,10" Grid.Column="2"/>
<Button x:Name="WeighingAButton" Content="Wiegen A" Width="120" <Button x:Name="WeighingAButton" Content="Wiegen A" Width="120"
Click="WeighingButton_Click" Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,42,10,10" Grid.Column="2"/> VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,10,10,10" Grid.Column="2"/>
<Button x:Name="WeighingBButton" Content="Wiegen B" Width="120" <Button x:Name="WeighingBButton" Content="Wiegen B" Width="120"
Click="WeighingButton_Click" Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,74,10,10" Grid.Column="2"/> VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,42,10,10" Grid.Column="2"/>
<Button x:Name="WeighingCButton" Content="Wiegen C" Width="120" <Button x:Name="WeighingCButton" Content="Wiegen C" Width="120"
Click="WeighingButton_Click" Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,106,10,10" Grid.Column="2"/> VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,74,10,10" Grid.Column="2"/>
<Button x:Name="WeighingDButton" Content="Wiegen D" Width="120" <Button x:Name="WeighingDButton" Content="Wiegen D" Width="120"
Click="WeighingButton_Click" Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,106,10,10" Grid.Column="2"/>
<Button x:Name="WeighingManualButton" Content="Handwiegung" Width="120"
Click="WeighingManualButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,138,10,10" Grid.Column="2"/> VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,138,10,10" Grid.Column="2"/>
</Grid> </Grid>
</GroupBox> </GroupBox>

View File

@ -1,8 +1,10 @@
using Elwig.Documents; using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Export;
using Elwig.Models; using Elwig.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.Win32;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -23,6 +25,7 @@ namespace Elwig.Windows {
private List<string> TextFilter = new(); private List<string> TextFilter = new();
private readonly RoutedCommand CtrlF = new(); private readonly RoutedCommand CtrlF = new();
private string? LastScaleError = null;
private string? ManualWeighingReason = null; private string? ManualWeighingReason = null;
private string? ScaleId = null; private string? ScaleId = null;
private string? WeighingId = null; private string? WeighingId = null;
@ -58,6 +61,8 @@ namespace Elwig.Windows {
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
SeasonInput.Value = Utils.CurrentLastSeason; SeasonInput.Value = Utils.CurrentLastSeason;
DoShowWarningWindows = false;
if (IsReceipt) { if (IsReceipt) {
Title = "Übernahme - Elwig"; Title = "Übernahme - Elwig";
TodayOnlyInput.IsChecked = true; TodayOnlyInput.IsChecked = true;
@ -71,6 +76,7 @@ namespace Elwig.Windows {
if (n >= 2) WeighingBButton.Content = $"Wiegen {App.Scales[1].ScaleId}"; if (n >= 2) WeighingBButton.Content = $"Wiegen {App.Scales[1].ScaleId}";
if (n >= 3) WeighingCButton.Content = $"Wiegen {App.Scales[2].ScaleId}"; if (n >= 3) WeighingCButton.Content = $"Wiegen {App.Scales[2].ScaleId}";
if (n >= 4) WeighingDButton.Content = $"Wiegen {App.Scales[3].ScaleId}"; if (n >= 4) WeighingDButton.Content = $"Wiegen {App.Scales[3].ScaleId}";
WeighingManualButton.Margin = new Thickness(10, 10 + n * 32, 10, 10);
} else { } else {
WeighingManualButton.Visibility = Visibility.Hidden; WeighingManualButton.Visibility = Visibility.Hidden;
WeighingAButton.Visibility = Visibility.Hidden; WeighingAButton.Visibility = Visibility.Hidden;
@ -87,13 +93,13 @@ namespace Elwig.Windows {
AllSeasonsInput.IsChecked = true; AllSeasonsInput.IsChecked = true;
} }
private async void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) {
OnSecondPassed(null, null); OnSecondPassed(null, null);
Timer.Start(); Timer.Start();
LockInputs(); LockInputs();
if (IsReceipt) { if (IsReceipt) {
NewDeliveryButton_Click(null, null); NewDeliveryButton_Click(null, null);
if ((await Context.Seasons.FindAsync(Utils.CurrentYear)) == null) { if ((Context.Seasons.Find(Utils.CurrentYear)) == null) {
MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.", MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.",
"Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Error); "Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Error);
} }
@ -102,20 +108,36 @@ namespace Elwig.Windows {
private async void Menu_Print_ShowDeliveryNote_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_ShowDeliveryNote_Click(object sender, RoutedEventArgs evt) {
if (DeliveryList.SelectedItem is not Delivery d) return; if (DeliveryList.SelectedItem is not Delivery d) return;
Mouse.OverrideCursor = Cursors.Wait;
using var doc = new DeliveryNote(d, Context); using var doc = new DeliveryNote(d, Context);
await doc.Generate(); await doc.Generate();
doc.Show(); doc.Show();
Mouse.OverrideCursor = null;
} }
private async void Menu_Print_PrintDeliveryNote_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_PrintDeliveryNote_Click(object sender, RoutedEventArgs evt) {
if (DeliveryList.SelectedItem is not Delivery d) return; if (DeliveryList.SelectedItem is not Delivery d) return;
Mouse.OverrideCursor = Cursors.Wait;
using var doc = new DeliveryNote(d, Context); using var doc = new DeliveryNote(d, Context);
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
await doc.Print(); await doc.Print();
} }
private void Menu_Export_Bki_Click(object sender, RoutedEventArgs evt) { private async void Menu_Export_Bki_Click(object sender, RoutedEventArgs evt) {
// TODO export Traubentransportscheinliste if (sender is not MenuItem m) return;
var year = int.Parse(m.Header.ToString()?.Split(" ")[^1] ?? Utils.CurrentLastSeason.ToString());
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Traubentransportscheinliste-{year}",
DefaultExt = Bki.FileExtension,
Title = $"Traubentransportscheinliste (BKI) speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
using var file = new Bki(d.FileName);
await file.ExportAsync(year);
Mouse.OverrideCursor = null;
}
} }
private void OnSecondPassed(object? sender, EventArgs? evt) { private void OnSecondPassed(object? sender, EventArgs? evt) {
@ -127,11 +149,16 @@ namespace Elwig.Windows {
} }
private void InitialInputs() { private void InitialInputs() {
LastScaleError = null;
WeighingId = null;
ScaleId = null;
ManualWeighingReason = null;
ClearOriginalValues(); ClearOriginalValues();
ClearDefaultValues(); ClearDefaultValues();
HandPickedInput.IsChecked = null; HandPickedInput.IsChecked = null;
if (App.Client.IsMatzen) { if (App.Client.IsMatzen || App.Client.IsWolkersdorf) {
GerebeltGewogenInput.IsChecked = true; GerebeltGewogenInput.IsChecked = true;
GerebeltGewogenInput.IsEnabled = false; GerebeltGewogenInput.IsEnabled = false;
SetDefaultValue(GerebeltGewogenInput); SetDefaultValue(GerebeltGewogenInput);
@ -141,6 +168,8 @@ namespace Elwig.Windows {
UnsetDefaultValue(GerebeltGewogenInput); UnsetDefaultValue(GerebeltGewogenInput);
} }
WineQualityLevelInput.IsEnabled = false;
SetDefaultValue(HandPickedInput); SetDefaultValue(HandPickedInput);
ValidateRequiredInputs(); ValidateRequiredInputs();
} }
@ -157,11 +186,21 @@ namespace Elwig.Windows {
bool ch = HasChanged, v = IsValid; bool ch = HasChanged, v = IsValid;
ResetButton.IsEnabled = ch; ResetButton.IsEnabled = ch;
SaveButton.IsEnabled = v && ch; SaveButton.IsEnabled = v && ch;
FinishButton.IsEnabled = v || !ch; FinishButton.IsEnabled = v && ch;
NewDeliveryPartButton.IsEnabled = v; NewDeliveryPartButton.IsEnabled = v && ch;
CancelCreatingButton.IsEnabled = DeliveryList.SelectedItem == null || DeliveryPartList.SelectedItem == null; CancelCreatingButton.IsEnabled = DeliveryList.SelectedItem == null || DeliveryPartList.SelectedItem == null;
} }
private void Input_KeyUp(object sender, KeyEventArgs evt) {
if (sender is not Control ctrl) return;
if (evt.Key != Key.Enter) return;
if (ctrl == MgNrInput || ctrl == MemberInput) {
SortIdInput.Focus(); SortIdInput.SelectAll();
} else if (ctrl == SortIdInput || ctrl == WineVarietyInput || ctrl == AttributesInput) {
GradationOeInput.Focus(); GradationOeInput.SelectAll();
}
}
private async Task RefreshDeliveryList() { private async Task RefreshDeliveryList() {
await RefreshDeliveryListQuery(); await RefreshDeliveryListQuery();
} }
@ -299,7 +338,8 @@ namespace Elwig.Windows {
.ToList(); .ToList();
} }
ControlUtils.RenewItemsSource(DeliveryList, deliveries, d => ((d as Delivery)?.Year, (d as Delivery)?.DId), DeliveryList_SelectionChanged, ControlUtils.RenewSourceDefault.IfOnly, !updateSort); ControlUtils.RenewItemsSource(DeliveryList, deliveries, d => ((d as Delivery)?.Year, (d as Delivery)?.DId),
DeliveryList_SelectionChanged, filter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
var members = deliveries.Select(d => d.Member).DistinctBy(m => m.MgNr).ToList(); var members = deliveries.Select(d => d.Member).DistinctBy(m => m.MgNr).ToList();
StatusMembers.Text = $"Mitglieder: {members.Count}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : ""); StatusMembers.Text = $"Mitglieder: {members.Count}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : "");
@ -382,10 +422,19 @@ namespace Elwig.Windows {
Title = $"Lieferungen - {Member.AdministrativeName} - Elwig"; Title = $"Lieferungen - {Member.AdministrativeName} - Elwig";
} }
Menu_Export_Bki.Items.Clear();
foreach (var s in await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync()) {
var i = new MenuItem {
Header = $"Season {s.Year}",
};
i.Click += Menu_Export_Bki_Click;
Menu_Export_Bki.Items.Add(i);
}
await RefreshDeliveryList(); await RefreshDeliveryList();
var d = DeliveryList.SelectedItem as Delivery; var d = DeliveryList.SelectedItem as Delivery;
var y = d?.Year ?? Utils.CurrentLastSeason; var y = d?.Year ?? Utils.CurrentLastSeason;
ControlUtils.RenewItemsSource(MemberInput, await Context.Members.OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr); ControlUtils.RenewItemsSource(MemberInput, await Context.Members.Where(m => m.IsActive || !IsCreating).OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr);
ControlUtils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId); ControlUtils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId);
ControlUtils.RenewItemsSource(WineVarietyInput, await Context.WineVarieties.OrderBy(v => v.Name).ToListAsync(), i => (i as WineVar)?.SortId); ControlUtils.RenewItemsSource(WineVarietyInput, await Context.WineVarieties.OrderBy(v => v.Name).ToListAsync(), i => (i as WineVar)?.SortId);
ControlUtils.RenewItemsSource(AttributesInput, await Context.WineAttributes.Where(a => IsCreating || a.IsActive).OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId); ControlUtils.RenewItemsSource(AttributesInput, await Context.WineAttributes.Where(a => IsCreating || a.IsActive).OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId);
@ -399,6 +448,7 @@ namespace Elwig.Windows {
if (IsCreating) await UpdateLsNr(); if (IsCreating) await UpdateLsNr();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
RefreshInputs();
} }
private void FocusSearchInput(object sender, RoutedEventArgs evt) { private void FocusSearchInput(object sender, RoutedEventArgs evt) {
@ -479,6 +529,7 @@ namespace Elwig.Windows {
ScaleId = p?.ScaleId; ScaleId = p?.ScaleId;
WeighingId = p?.WeighingId; WeighingId = p?.WeighingId;
ManualWeighingReason = p?.WeighingReason;
FinishInputFilling(); FinishInputFilling();
} }
@ -540,8 +591,7 @@ namespace Elwig.Windows {
p.ManualWeighing = ManualWeighingInput.IsChecked ?? false; p.ManualWeighing = ManualWeighingInput.IsChecked ?? false;
p.ScaleId = ScaleId; p.ScaleId = ScaleId;
p.WeighingId = WeighingId; p.WeighingId = WeighingId;
if (ManualWeighingReason != null) p.WeighingReason = ManualWeighingReason;
p.Comment = (p.Comment != null ? $"{p.Comment} / " : "") + $"Begründung Handwiegung: {ManualWeighingReason}";
EntityEntry<Delivery>? dEntry = null; EntityEntry<Delivery>? dEntry = null;
EntityEntry<DeliveryPart>? pEntry = null; EntityEntry<DeliveryPart>? pEntry = null;
@ -607,12 +657,18 @@ namespace Elwig.Windows {
ScaleId = null; ScaleId = null;
WeighingId = null; WeighingId = null;
} }
ManualWeighingReason = null; LastScaleError = null;
ManualWeighingInput.IsChecked = false;
} catch (Exception e) { } catch (Exception e) {
LastScaleError = e.Message.Split(": ")[^1];
WeightInput.Text = "";
ScaleId = null;
WeighingId = null;
MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{e.Message}", "Waagenfehler", MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error); MessageBoxButton.OK, MessageBoxImage.Error);
} }
ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false;
base.TextBox_TextChanged(WeightInput, null);
EnableWeighingButtons(); EnableWeighingButtons();
UpdateButtons(); UpdateButtons();
} }
@ -688,7 +744,7 @@ namespace Elwig.Windows {
MemberInput.SelectedItem = valid ? Context.Members.Find(int.Parse(MgNrInput.Text)) : null; MemberInput.SelectedItem = valid ? Context.Members.Find(int.Parse(MgNrInput.Text)) : null;
} }
private void MemberInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) { private void MemberInput_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
var m = MemberInput.SelectedItem as Member; var m = MemberInput.SelectedItem as Member;
if (m != null) MgNrInput.Text = m.MgNr.ToString(); if (m != null) MgNrInput.Text = m.MgNr.ToString();
MemberAddressField.Text = m?.FullAddress; MemberAddressField.Text = m?.FullAddress;
@ -702,32 +758,54 @@ namespace Elwig.Windows {
} }
private async void NewDeliveryPartButton_Click(object sender, RoutedEventArgs evt) { private async void NewDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
FinishButton.IsEnabled = false;
NewDeliveryPartButton.IsEnabled = false;
NewDeliveryPartButton.Cursor = Cursors.Wait;
DeliveryPartList.IsEnabled = false; DeliveryPartList.IsEnabled = false;
var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart); var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart);
await RefreshDeliveryList(); await RefreshDeliveryList();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
NewDeliveryPartButton.Cursor = null;
DeliveryList.SelectedItem = p?.Delivery; DeliveryList.SelectedItem = p?.Delivery;
DeliveryPartList.SelectedItem = null; DeliveryPartList.SelectedItem = null;
RefreshInputs();
InitialInputs(); InitialInputs();
} }
private async void FinishButton_Click(object sender, RoutedEventArgs evt) { private async void FinishButton_Click(object sender, RoutedEventArgs evt) {
FinishButton.IsEnabled = false;
NewDeliveryPartButton.IsEnabled = false;
FinishButton.Cursor = Cursors.Wait;
DeliveryPartList.IsEnabled = false; DeliveryPartList.IsEnabled = false;
var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart); var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart);
await RefreshDeliveryList(); await RefreshDeliveryList();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
if (p?.Delivery != null) { if (p?.Delivery != null) {
Mouse.OverrideCursor = Cursors.Wait;
using var doc = new DeliveryNote(p.Delivery, Context); using var doc = new DeliveryNote(p.Delivery, Context);
await doc.Generate(); await doc.Generate();
doc.Show(); Mouse.OverrideCursor = null;
//await doc.Print(2); if (App.Config.Debug) {
doc.Show();
} else {
await doc.Print(2);
}
} }
FinishButton.Cursor = null;
DeliveryList.SelectedItem = null; DeliveryList.SelectedItem = null;
RefreshInputs();
InitInputs(); InitInputs();
} }
private void CancelCreatingButton_Click(object sender, RoutedEventArgs evt) { private async void CancelCreatingButton_Click(object sender, RoutedEventArgs evt) {
ControlUtils.RenewItemsSource(AttributesInput, Context.WineAttributes.OrderBy(a => a.Name).ToList(), i => (i as WineAttr)?.AttrId); if (IsCreating && HasChanged) {
var r = MessageBox.Show("Soll der Vorgang wirklich abgebrochen werden?", "Abbrechen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) return;
}
ControlUtils.RenewItemsSource(AttributesInput, await Context.WineAttributes.OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId);
ControlUtils.RenewItemsSource(MemberInput, await Context.Members.Where(m => m.IsActive || !IsReceipt).OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr);
if (DeliveryList.SelectedItem is not Delivery d) { if (DeliveryList.SelectedItem is not Delivery d) {
// switch away from creating mode // switch away from creating mode
IsCreating = false; IsCreating = false;
@ -748,8 +826,11 @@ namespace Elwig.Windows {
} }
} }
private void NewDeliveryButton_Click(object? sender, RoutedEventArgs? evt) { private async void NewDeliveryButton_Click(object? sender, RoutedEventArgs? evt) {
ControlUtils.RenewItemsSource(AttributesInput, Context.WineAttributes.Where(a => a.IsActive).OrderBy(a => a.Name).ToList(), i => (i as WineAttr)?.AttrId); TodayOnlyInput.IsChecked = true;
SearchInput.Text = "";
ControlUtils.RenewItemsSource(AttributesInput, await Context.WineAttributes.Where(a => a.IsActive).OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId);
ControlUtils.RenewItemsSource(MemberInput, await Context.Members.Where(m => m.IsActive || !IsReceipt).OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr);
IsCreating = true; IsCreating = true;
DeliveryList.IsEnabled = false; DeliveryList.IsEnabled = false;
DeliveryPartList.IsEnabled = false; DeliveryPartList.IsEnabled = false;
@ -769,6 +850,7 @@ namespace Elwig.Windows {
try { try {
if (res == null || res <= 0) if (res == null || res <= 0)
return; return;
Mouse.OverrideCursor = Cursors.Wait;
ClearOriginalValues(); ClearOriginalValues();
if (res >= p.Weight) { if (res >= p.Weight) {
ControlUtils.SelectComboBoxItem(WineQualityLevelInput, q => (q as WineQualLevel)?.QualId, "WEI"); ControlUtils.SelectComboBoxItem(WineQualityLevelInput, q => (q as WineQualLevel)?.QualId, "WEI");
@ -797,6 +879,7 @@ namespace Elwig.Windows {
} }
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
Mouse.OverrideCursor = null;
FinishInputFilling(); FinishInputFilling();
} catch (Exception exc) { } catch (Exception exc) {
if (entry1 != null) { if (entry1 != null) {
@ -814,7 +897,7 @@ namespace Elwig.Windows {
} }
private void WeighingManualButton_Click(object sender, RoutedEventArgs evt) { private void WeighingManualButton_Click(object sender, RoutedEventArgs evt) {
var res = Utils.ShowManualWeighingDialog(); var res = Utils.ShowManualWeighingDialog(LastScaleError);
if (res == null) return; if (res == null) return;
WeightInput.Text = $"{res?.Item1:N0}"; WeightInput.Text = $"{res?.Item1:N0}";
ManualWeighingInput.IsChecked = true; ManualWeighingInput.IsChecked = true;
@ -849,21 +932,28 @@ namespace Elwig.Windows {
$"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?", $"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?",
"Lieferung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); "Lieferung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) { if (r == MessageBoxResult.Yes) {
Mouse.OverrideCursor = Cursors.Wait;
Context.Remove(d); Context.Remove(d);
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
await RefreshDeliveryList(); await RefreshDeliveryList();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
Mouse.OverrideCursor = null;
} }
} }
private async void SaveButton_Click(object sender, RoutedEventArgs evt) { private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
DeliveryPart p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart); SaveButton.IsEnabled = false;
SaveButton.Cursor = Cursors.Wait;
IsEditing = false; IsEditing = false;
IsCreating = false; IsCreating = false;
DeliveryList.IsEnabled = true; DeliveryList.IsEnabled = true;
DeliveryPartList.IsEnabled = true; DeliveryPartList.IsEnabled = true;
DeliveryPart p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart);
SaveButton.Cursor = null;
HideSaveResetCancelButtons(); HideSaveResetCancelButtons();
ShowNewEditDeleteButtons(); ShowNewEditDeleteButtons();
LockInputs(); LockInputs();
@ -924,13 +1014,13 @@ namespace Elwig.Windows {
.Select(d => d.LsNr) .Select(d => d.LsNr)
.ToListAsync(); .ToListAsync();
var res = Utils.ShowDeliveryExtractionDialog($"{delivery.LsNr}/{p.DPNr}", delivery.Member.AdministrativeName, count == 1, lsnrs); var res = Utils.ShowDeliveryExtractionDialog($"{delivery.LsNr}/{p.DPNr}", delivery.Member.AdministrativeName, count == 1, lsnrs);
if (res == null) return;
EntityEntry<Delivery>? entry = null; EntityEntry<Delivery>? entry = null;
try { try {
Delivery? d = null; Delivery? d = null;
if (res == null) { Mouse.OverrideCursor = Cursors.Wait;
return; if (res == "new") {
} else if (res == "new") {
d = Context.CreateProxy<Delivery>(); d = Context.CreateProxy<Delivery>();
d.Date = delivery.Date; d.Date = delivery.Date;
d.Time = delivery.Time; d.Time = delivery.Time;
@ -958,6 +1048,7 @@ namespace Elwig.Windows {
await Context.Entry(p).ReloadAsync(); await Context.Entry(p).ReloadAsync();
await Context.Entry(delivery).ReloadAsync(); await Context.Entry(delivery).ReloadAsync();
Mouse.OverrideCursor = null;
await RefreshDeliveryList(); await RefreshDeliveryList();
DeliveryList.SelectedItem = d; DeliveryList.SelectedItem = d;
} catch (Exception exc) { } catch (Exception exc) {
@ -979,9 +1070,11 @@ namespace Elwig.Windows {
$"Soll die Teillieferung Nr. {p.DPNr} wirklich unwiderruflich gelöscht werden?", $"Soll die Teillieferung Nr. {p.DPNr} wirklich unwiderruflich gelöscht werden?",
"Lieferung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); "Lieferung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) { if (r == MessageBoxResult.Yes) {
Mouse.OverrideCursor = Cursors.Wait;
Context.Remove(p); Context.Remove(p);
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
Mouse.OverrideCursor = null;
} }
} }
@ -1026,8 +1119,8 @@ namespace Elwig.Windows {
} }
private void ShowFinishNewPartDeliveryCancelButtons() { private void ShowFinishNewPartDeliveryCancelButtons() {
FinishButton.IsEnabled = IsCreating && IsValid; FinishButton.IsEnabled = false;
NewDeliveryPartButton.IsEnabled = IsCreating && IsValid; NewDeliveryPartButton.IsEnabled = false;
CancelCreatingButton.IsEnabled = true; CancelCreatingButton.IsEnabled = true;
FinishButton.Visibility = Visibility.Visible; FinishButton.Visibility = Visibility.Visible;
NewDeliveryPartButton.Visibility = Visibility.Visible; NewDeliveryPartButton.Visibility = Visibility.Visible;
@ -1082,10 +1175,11 @@ namespace Elwig.Windows {
private void EnableWeighingButtons() { private void EnableWeighingButtons() {
WeighingManualButton.IsEnabled = true; WeighingManualButton.IsEnabled = true;
WeighingAButton.IsEnabled = true; var n = App.Scales.Count;
WeighingBButton.IsEnabled = true; WeighingAButton.IsEnabled = n > 0 && App.Scales[0].IsReady;
WeighingCButton.IsEnabled = true; WeighingBButton.IsEnabled = n > 1 && App.Scales[1].IsReady;
WeighingDButton.IsEnabled = true; WeighingCButton.IsEnabled = n > 2 && App.Scales[2].IsReady;
WeighingDButton.IsEnabled = n > 3 && App.Scales[3].IsReady;
} }
private async Task UpdateLsNr() { private async Task UpdateLsNr() {
@ -1196,8 +1290,10 @@ namespace Elwig.Windows {
} }
private void ModifiersInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) { private void ModifiersInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) {
if ((IsEditing || IsCreating) && App.Client.IsMatzen) { if (!IsEditing && !IsCreating) return;
var mod = ModifiersInput.SelectedItems.Cast<Modifier>(); var mod = ModifiersInput.SelectedItems.Cast<Modifier>();
var source = ModifiersInput.ItemsSource.Cast<Modifier>();
if (App.Client.IsMatzen) {
var kl = mod.Where(m => m.Name.StartsWith("Klasse ")); var kl = mod.Where(m => m.Name.StartsWith("Klasse "));
if (kl.Count() > 1) { if (kl.Count() > 1) {
foreach (var r in kl.Take(kl.Count() - 1)) ModifiersInput.SelectedItems.Remove(r); foreach (var r in kl.Take(kl.Count() - 1)) ModifiersInput.SelectedItems.Remove(r);
@ -1206,11 +1302,14 @@ namespace Elwig.Windows {
} }
private void LesewagenInput_Changed(object sender, RoutedEventArgs evt) { private void LesewagenInput_Changed(object sender, RoutedEventArgs evt) {
if ((IsEditing || IsCreating) && App.Client.IsMatzen) { if (!IsEditing && !IsCreating) return;
var mod = ModifiersInput.SelectedItems.Cast<Modifier>(); var mod = ModifiersInput.SelectedItems.Cast<Modifier>();
var source = ModifiersInput.ItemsSource.Cast<Modifier>();
var lw = LesewagenInput.IsChecked == true;
if (App.Client.IsMatzen) {
var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("A")[0]; var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("A")[0];
if (LesewagenInput.IsChecked == true) kl++; else kl--; if (lw) kl++; else kl--;
var newKl = ModifiersInput.ItemsSource.Cast<Modifier>().Where(m => m.ModId == kl.ToString()).FirstOrDefault(); var newKl = source.Where(m => m.ModId == kl.ToString()).FirstOrDefault();
if (newKl != null) ModifiersInput.SelectedItems.Add(newKl); if (newKl != null) ModifiersInput.SelectedItems.Add(newKl);
} }
CheckBox_Changed(sender, evt); CheckBox_Changed(sender, evt);

View File

@ -23,7 +23,7 @@
<MenuItem Header="Über"/> <MenuItem Header="Über"/>
</MenuItem> </MenuItem>
</Menu> </Menu>
<Grid Height="100" VerticalAlignment="Top" Margin="0,25,0,0"> <Grid Height="100" VerticalAlignment="Top" Margin="25,25,0,0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@ -38,26 +38,20 @@
HorizontalAlignment="Left" Margin="0,70,0,0" VerticalAlignment="Top"/> HorizontalAlignment="Left" Margin="0,70,0,0" VerticalAlignment="Top"/>
</Grid> </Grid>
<Button x:Name="MemberAdminButton" Content="Mitgliederverwaltung" Click="MemberAdminButton_Click" <Button x:Name="MemberAdminButton" Content="Mitglieder" Click="MemberAdminButton_Click"
Margin="50,160,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,160,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="MemberListButton" Content="Mitgliederliste" Click="MemberListButton_Click"
Margin="50,200,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="ReceiptButton" Content="Übernahme" Click="ReceiptButton_Click" <Button x:Name="ReceiptButton" Content="Übernahme" Click="ReceiptButton_Click"
Margin="50,200,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="DeliveryAdminButton" Content="Lieferungen" Click="DeliveryAdminButton_Click"
Margin="50,240,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,240,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="DeliveryAdminButton" Content="Lieferungsverwaltung" Click="DeliveryAdminButton_Click" <Button x:Name="PaymentWindowButton" Content="Auszahlung" Click="PaymentWindowButton_Click"
Margin="50,280,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,280,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="DeliveryListButton" Content="Lieferungungen" Click="DeliveryListButton_Click" IsEnabled="False" <Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click"
Margin="50,320,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,320,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click"
Margin="260,160,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="PdfButton" Content="PDF Erzeugen" Click="PdfButton_Click" Tag="Print"
Margin="260,200,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="TestWindowButton" Content="Test Fenster" Click="TestWindowButton_Click" <Button x:Name="TestWindowButton" Content="Test Fenster" Click="TestWindowButton_Click"
Margin="260,240,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="QueryWindowButton" Content="Datenbankabfragen" Click="QueryWindowButton_Click"
Margin="260,280,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="260,280,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="PaymentWindowButton" Content="Auszahlung" Click="PaymentWindowButton_Click" <Button x:Name="QueryWindowButton" Content="Datenbankabfragen" Click="QueryWindowButton_Click"
Margin="260,320,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="260,320,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</Grid> </Grid>
</Window> </Window>

View File

@ -1,18 +1,17 @@
using System.Linq;
using System.Windows; using System.Windows;
using Elwig.Documents;
using Elwig.Helpers;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class MainWindow : Window { public partial class MainWindow : Window {
public MainWindow() { public MainWindow() {
InitializeComponent(); InitializeComponent();
if (!App.Config.Debug) {
TestWindowButton.Visibility = Visibility.Hidden;
//QueryWindowButton.Visibility = Visibility.Hidden;
}
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) { }
PdfButton.IsEnabled = App.IsPrintingReady;
}
private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) { private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) {
var w = new MemberAdminWindow(); var w = new MemberAdminWindow();
@ -38,15 +37,6 @@ namespace Elwig.Windows {
// TODO // TODO
} }
private void PdfButton_Click(object sender, RoutedEventArgs evt) {
Utils.RunBackground("PDF Generation", async () => {
using var ctx = new AppDbContext();
using var doc = new DeliveryNote(ctx.Deliveries.OrderBy(d => d.Parts.Count).ThenBy(d => d.Year).ThenBy(d => d.DId).Last(), ctx);
await doc.Generate();
doc.Show();
});
}
private void TestWindowButton_Click(object sender, RoutedEventArgs evt) { private void TestWindowButton_Click(object sender, RoutedEventArgs evt) {
var w = new TestWindow(); var w = new TestWindow();
w.Show(); w.Show();

View File

@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
Title="Mitgliederverwaltung - Elwig" Height="670" Width="1250" MinHeight="600" MinWidth="1000" Title="Mitglieder - Elwig" Height="670" Width="1250" MinHeight="600" MinWidth="1000"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
@ -178,60 +178,64 @@
<ColumnDefinition Width="2*"/> <ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Content="E-Mail-Adresse:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="E-Mail-Adresse (1):" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddressInput" Margin="0,10,10,0" Grid.Column="1" Grid.ColumnSpan="2" <TextBox x:Name="EmailAddress1Input" Margin="0,10,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/> TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<ComboBox x:Name="PhoneNr1TypeInput" DisplayMemberPath="Value" Margin="6,40,5,0" FontSize="12" Padding="6,4,4,4"/> <Label Content="E-Mail-Adresse (2):" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="PhoneNr1Input" Margin="0,40,5,0" Grid.Column="1" <TextBox x:Name="EmailAddress2Input" Margin="0,40,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<ComboBox x:Name="PhoneNr1TypeInput" DisplayMemberPath="Value" Margin="6,70,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr1Input" Margin="0,70,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr1CommentInput" Margin="0,40,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr1CommentInput" Margin="0,70,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr2TypeInput" DisplayMemberPath="Value" Margin="6,70,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr2TypeInput" DisplayMemberPath="Value" Margin="6,100,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr2Input" Margin="0,70,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr2Input" Margin="0,100,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr2CommentInput" Margin="0,70,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr2CommentInput" Margin="0,100,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr3TypeInput" DisplayMemberPath="Value" Margin="6,100,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr3TypeInput" DisplayMemberPath="Value" Margin="6,130,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr3Input" Margin="0,100,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr3Input" Margin="0,130,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr3CommentInput" Margin="0,100,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr3CommentInput" Margin="0,130,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr4TypeInput" DisplayMemberPath="Value" Margin="6,130,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr4TypeInput" DisplayMemberPath="Value" Margin="6,160,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr4Input" Margin="0,130,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr4Input" Margin="0,160,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr4CommentInput" Margin="0,130,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr4CommentInput" Margin="0,160,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr5TypeInput" DisplayMemberPath="Value" Margin="6,160,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr5TypeInput" DisplayMemberPath="Value" Margin="6,190,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr5Input" Margin="0,160,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr5Input" Margin="0,190,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr5CommentInput" Margin="0,160,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr5CommentInput" Margin="0,190,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr6TypeInput" DisplayMemberPath="Value" Margin="6,190,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr6TypeInput" DisplayMemberPath="Value" Margin="6,220,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr6Input" Margin="0,190,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr6Input" Margin="0,220,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr6CommentInput" Margin="0,190,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr6CommentInput" Margin="0,220,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr7TypeInput" DisplayMemberPath="Value" Margin="6,220,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr7TypeInput" DisplayMemberPath="Value" Margin="6,250,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr7Input" Margin="0,220,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr7Input" Margin="0,250,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr7CommentInput" Margin="0,220,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr7CommentInput" Margin="0,250,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr8TypeInput" DisplayMemberPath="Value" Margin="6,250,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr8TypeInput" DisplayMemberPath="Value" Margin="6,280,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr8Input" Margin="0,250,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr8Input" Margin="0,280,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr8CommentInput" Margin="0,250,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr8CommentInput" Margin="0,280,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr9TypeInput" DisplayMemberPath="Value" Margin="6,280,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr9TypeInput" DisplayMemberPath="Value" Margin="6,310,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr9Input" Margin="0,280,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr9Input" Margin="0,310,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr9CommentInput" Margin="0,280,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr9CommentInput" Margin="0,280,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>

View File

@ -89,7 +89,8 @@ namespace Elwig.Windows {
.ToList(); .ToList();
} }
ControlUtils.RenewItemsSource(MemberList, members, i => (i as Member)?.MgNr, MemberList_SelectionChanged, ControlUtils.RenewSourceDefault.IfOnly, !updateSort); ControlUtils.RenewItemsSource(MemberList, members, i => (i as Member)?.MgNr,
MemberList_SelectionChanged, TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
} }
private void RefreshInputs(bool validate = false) { private void RefreshInputs(bool validate = false) {
@ -265,7 +266,7 @@ namespace Elwig.Windows {
} }
private void Menu_Member_SendEmail_Click(object sender, RoutedEventArgs evt) { private void Menu_Member_SendEmail_Click(object sender, RoutedEventArgs evt) {
Utils.MailTo(((Member)MemberList.SelectedItem).Email); Utils.MailTo(((Member)MemberList.SelectedItem).EmailAddresses.First().Address);
} }
private void FocusSearchInput(object sender, RoutedEventArgs evt) { private void FocusSearchInput(object sender, RoutedEventArgs evt) {
@ -348,8 +349,6 @@ namespace Elwig.Windows {
m.PostalDestId = ((AT_PlzDest)OrtInput.SelectedItem).Id; m.PostalDestId = ((AT_PlzDest)OrtInput.SelectedItem).Id;
m.Address = AddressInput.Text; m.Address = AddressInput.Text;
m.Email = (EmailAddressInput.Text == "") ? null : EmailAddressInput.Text;
m.Iban = (IbanInput.Text == "") ? null : IbanInput.Text.Replace(" ", ""); m.Iban = (IbanInput.Text == "") ? null : IbanInput.Text.Replace(" ", "");
m.Bic = (BicInput.Text == "") ? null : BicInput.Text; m.Bic = (BicInput.Text == "") ? null : BicInput.Text;
@ -423,6 +422,27 @@ namespace Elwig.Windows {
} }
} }
for (int i = 0; i < 2; i++) {
var input = i == 0 ? EmailAddress1Input : EmailAddress2Input;
var emailAddr = m.EmailAddresses.FirstOrDefault(a => a.Nr - 1 == i);
if (input.Text == "") {
if (emailAddr != null) {
Context.Remove(emailAddr);
}
} else {
MemberEmailAddr a = emailAddr ?? Context.CreateProxy<MemberEmailAddr>();
a.Nr = i + 1;
a.Address = input.Text;
a.Comment = null;
if (emailAddr == null) {
a.MgNr = newMgNr;
await Context.AddAsync(a);
} else {
Context.Update(a);
}
}
}
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
if (newMgNr != m.MgNr) { if (newMgNr != m.MgNr) {
@ -474,7 +494,10 @@ namespace Elwig.Windows {
OrtInput.SelectedItem = null; OrtInput.SelectedItem = null;
} }
EmailAddressInput.Text = m.Email; var emailAddrs = m.EmailAddresses.OrderBy(a => a.Nr).ToList();
EmailAddress1Input.Text = emailAddrs.Count > 0 ? emailAddrs[0].Address : "";
EmailAddress2Input.Text = emailAddrs.Count > 1 ? emailAddrs[1].Address : "";
var phoneNrs = m.TelephoneNumbers.OrderBy(p => p.Nr).ToList(); var phoneNrs = m.TelephoneNumbers.OrderBy(p => p.Nr).ToList();
for (int i = 0; i < PhoneNrInputs.Length; i++) { for (int i = 0; i < PhoneNrInputs.Length; i++) {
if (i < phoneNrs.Count) { if (i < phoneNrs.Count) {
@ -524,7 +547,7 @@ namespace Elwig.Windows {
AreaCommitment.Text = $"{m.ActiveAreaCommitments.Select(c => c.Area).Sum():N0} m²"; AreaCommitment.Text = $"{m.ActiveAreaCommitments.Select(c => c.Area).Sum():N0} m²";
Menu_Member_SendEmail.IsEnabled = m.Email != null; Menu_Member_SendEmail.IsEnabled = m.EmailAddresses.Count > 0;
FinishInputFilling(); FinishInputFilling();
} }

View File

@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="Test Fenster - Elwig" MinHeight="400" MinWidth="325" Height="450" Width="800" ResizeMode="CanResize"> Title="Test Fenster - Elwig" MinHeight="400" MinWidth="325" Height="450" Width="800" ResizeMode="CanResize" Loaded="Window_Loaded">
<Grid> <Grid>
<xctk:CheckComboBox x:Name="MyComboBox" HorizontalAlignment="Left" Margin="216,186,0,0" VerticalAlignment="Top" Delimiter=", " <xctk:CheckComboBox x:Name="MyComboBox" HorizontalAlignment="Left" Margin="216,186,0,0" VerticalAlignment="Top" Delimiter=", "
SelectedValue="{Binding SelectedValue}" SelectedValue="{Binding SelectedValue}"
@ -18,5 +18,10 @@
<TextBlock x:Name="Output" Height="20" Width="200" Margin="470,329,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/> <TextBlock x:Name="Output" Height="20" Width="200" Margin="470,329,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Button x:Name="ChartButton" Content="Chart" Click="ChartButton_Click" <Button x:Name="ChartButton" Content="Chart" Click="ChartButton_Click"
Margin="50,240,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,240,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="PdfDeliveryButton" Content="Lieferschein Erzeugen" Click="PdfDeliveryButton_Click" Tag="Print"
Margin="260,190,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="PdfCreditButton" Content="Gutschrift Erzeugen" Click="PdfCreditButton_Click" Tag="Print"
Margin="260,160,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</Grid> </Grid>
</Window> </Window>

View File

@ -1,16 +1,26 @@
using Elwig.Documents;
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Input;
using Xceed.Wpf.Toolkit.Primitives; using Xceed.Wpf.Toolkit.Primitives;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class TestWindow : Window { public partial class TestWindow : Window {
public TestWindow() { public TestWindow() {
InitializeComponent(); InitializeComponent();
MyComboBox.ItemsSource = new string[] { "Klasse A" , "Klasse B", "Klasse C", "Klasse D", "Klasse E", "Klasse F" }; MyComboBox.ItemsSource = new string[] { "Klasse A" , "Klasse B", "Klasse C", "Klasse D", "Klasse E", "Klasse F" };
MyListBox.ItemsSource = new string[] { "Test 1", "Test 2", "Test 3", "Test 4" }; MyListBox.ItemsSource = new string[] { "Test 1", "Test 2", "Test 3", "Test 4" };
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) {
PdfDeliveryButton.IsEnabled = App.IsPrintingReady;
PdfCreditButton.IsEnabled = App.IsPrintingReady;
}
private void OnItemSelectionChanged(object sender, ItemSelectionChangedEventArgs e) { private void OnItemSelectionChanged(object sender, ItemSelectionChangedEventArgs e) {
MyText.Text = string.Join(", ", MyComboBox.SelectedItems.Cast<string>()); MyText.Text = string.Join(", ", MyComboBox.SelectedItems.Cast<string>());
} }
@ -39,5 +49,23 @@ namespace Elwig.Windows {
var w = new ChartWindow(); var w = new ChartWindow();
w.Show(); w.Show();
} }
private async void PdfDeliveryButton_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.Wait;
using var ctx = new AppDbContext();
using var doc = new DeliveryNote(await ctx.Deliveries.OrderBy(d => d.Parts.Count).ThenBy(d => d.Year).ThenBy(d => d.DId).LastAsync(), ctx);
await doc.Generate();
doc.Show();
Mouse.OverrideCursor = null;
}
private async void PdfCreditButton_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.Wait;
using var ctx = new AppDbContext();
using var doc = new CreditNote(await ctx.Credits.FirstAsync(), ctx);
await doc.Generate();
doc.Show();
Mouse.OverrideCursor = null;
}
} }
} }

View File

@ -1,6 +1,5 @@
::mkdir "C:\Program Files\Elwig" ::mkdir "C:\Program Files\Elwig"
::curl -s "http://www.columbia.edu/~em36/PDFtoPrinter.exe" -z "C:\Program Files\Elwig\PDFtoPrinter.exe" -o "C:\Program Files\Elwig\PDFtoPrinter.exe" ::curl -s "http://www.columbia.edu/~em36/PDFtoPrinter.exe" -z "C:\Program Files\Elwig\PDFtoPrinter.exe" -o "C:\Program Files\Elwig\PDFtoPrinter.exe"
mkdir "C:\ProgramData\Elwig\resources" mkdir "C:\ProgramData\Elwig\resources"
curl -s -L "https://unpkg.com/pagedjs/dist/paged.polyfill.js" -o "C:\ProgramData\Elwig\resources\paged.polyfill.js" copy /b /y Documents\*.css "C:\ProgramData\Elwig\resources"
copy /b /y "Documents\style.css" "C:\ProgramData\Elwig\resources\style.css"
copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources" copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources"

View File

@ -1,12 +0,0 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<ComponentGroup Id="DocumentTemplateComponents" Directory="ConfigFolderResources">
<Component>
<File Source="$(TargetDir)\paged.polyfill.js" />
</Component>
<Component>
<File Source="$(var.ElwigProjectDir)\Documents\style.css" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>

View File

@ -23,10 +23,10 @@
--> -->
<xsl:key <xsl:key
name="FileToRemove" name="FileToRemove"
match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 6 ) != '.cshtml' ]" match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 2 ) = '.cs' ]"
use="@Id" use="@Id"
/> />
<!-- Get the last 4 characters of a string using `substring( s, len(s) - 3 )`, it uses -3 and not -4 because XSLT uses 1-based indexes, not 0-based indexes. --> <!-- Get the last 3 characters of a string using `substring( s, len(s) - 2 )`, it uses -2 and not -3 because XSLT uses 1-based indexes, not 0-based indexes. -->
<!-- By default, copy all elements and nodes into the output... --> <!-- By default, copy all elements and nodes into the output... -->
<xsl:template match="@*|node()"> <xsl:template match="@*|node()">

View File

@ -2,6 +2,7 @@
[general] [general]
; Only needed, if more than one branch is stored in database ; Only needed, if more than one branch is stored in database
branch = Zweigstelle branch = Zweigstelle
;debug = true
[database] [database]
; Relative or absolute path to database file ; Relative or absolute path to database file

View File

@ -25,14 +25,13 @@
</Task> </Task>
</UsingTask> </UsingTask>
<Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild"> <Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild">
<Exec Command="curl -s -L &quot;https://unpkg.com/pagedjs/dist/paged.polyfill.js&quot; -o &quot;$(TargetDir)paged.polyfill.js&quot;" />
<Exec Command="curl -s &quot;http://www.columbia.edu/~em36/PDFtoPrinter.exe&quot; -z &quot;$(TargetDir)PDFtoPrinter.exe&quot; -o &quot;$(TargetDir)PDFtoPrinter.exe&quot;" /> <Exec Command="curl -s &quot;http://www.columbia.edu/~em36/PDFtoPrinter.exe&quot; -z &quot;$(TargetDir)PDFtoPrinter.exe&quot; -o &quot;$(TargetDir)PDFtoPrinter.exe&quot;" />
<Exec Command="dotnet publish &quot;$(SolutionDir)Elwig\Elwig.csproj&quot; &quot;/p:PublishProfile=$(SolutionDir)\Elwig\Properties\PublishProfiles\FolderProfile.pubxml&quot;" /> <Exec Command="dotnet publish &quot;$(SolutionDir)Elwig\Elwig.csproj&quot; &quot;/p:PublishProfile=$(SolutionDir)\Elwig\Properties\PublishProfiles\FolderProfile.pubxml&quot;" />
<GetFileVersion AssemblyPath="..\Elwig\bin\Publish\Elwig.exe"> <GetFileVersion AssemblyPath="..\Elwig\bin\Publish\Elwig.exe">
<Output TaskParameter="Version" PropertyName="ElwigFileVersion" /> <Output TaskParameter="Version" PropertyName="ElwigFileVersion" />
</GetFileVersion> </GetFileVersion>
<PropertyGroup> <PropertyGroup>
<DefineConstants>ProductVersion=$(ElwigFileVersion);BuildPath=..\Elwig\bin\Publish;DocumentTemplatesPath=..\Elwig\Documents;ElwigProjectDir=..\Elwig</DefineConstants> <DefineConstants>ProductVersion=$(ElwigFileVersion);BuildPath=..\Elwig\bin\Publish;DocumentPath=..\Elwig\Documents;ElwigProjectDir=..\Elwig</DefineConstants>
</PropertyGroup> </PropertyGroup>
</Target> </Target>
<ItemGroup> <ItemGroup>
@ -50,13 +49,13 @@
<ComponentGroupName>DocumentTemplates</ComponentGroupName> <ComponentGroupName>DocumentTemplates</ComponentGroupName>
<DirectoryRefId>ConfigFolderResources</DirectoryRefId> <DirectoryRefId>ConfigFolderResources</DirectoryRefId>
<SuppressRootDirectory>true</SuppressRootDirectory> <SuppressRootDirectory>true</SuppressRootDirectory>
<PreprocessorVariable>DocumentTemplatesPath</PreprocessorVariable> <PreprocessorVariable>DocumentPath</PreprocessorVariable>
<Transforms>DocumentTemplatesTransform.xslt</Transforms> <Transforms>DocumentTransform.xslt</Transforms>
</HarvestDirectory> </HarvestDirectory>
<BindPath BindName="DocumentTemplateBindPath" Include="../Elwig/Documents" /> <BindPath BindName="DocumentTemplateBindPath" Include="../Elwig/Documents" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="DocumentTemplatesTransform.xslt" /> <None Include="DocumentTransform.xslt" />
<None Include="BuildFilesTransform.xslt" /> <None Include="BuildFilesTransform.xslt" />
<None Include="Files\config.ini" /> <None Include="Files\config.ini" />
</ItemGroup> </ItemGroup>

View File

@ -11,7 +11,6 @@
<ComponentGroupRef Id="MainComponents"/> <ComponentGroupRef Id="MainComponents"/>
<ComponentGroupRef Id="BuildFiles"/> <ComponentGroupRef Id="BuildFiles"/>
<ComponentGroupRef Id="DocumentTemplates"/> <ComponentGroupRef Id="DocumentTemplates"/>
<ComponentGroupRef Id="DocumentTemplateComponents"/>
</Feature> </Feature>
</Package> </Package>
</Wix> </Wix>

View File

@ -68,5 +68,37 @@ namespace Tests {
Assert.Throws<ArgumentException>(() => Utils.Modulo("789", -1)); Assert.Throws<ArgumentException>(() => Utils.Modulo("789", -1));
}); });
} }
[Test]
public void Test_SplitAddress() {
Assert.Multiple(() => {
Assert.That(Utils.SplitAddress("Winzerstraße 1"), Is.EqualTo(("Winzerstraße", "1")));
Assert.That(Utils.SplitAddress("Auf dem Feld 12"), Is.EqualTo(("Auf dem Feld", "12")));
Assert.That(Utils.SplitAddress("Winzerstraße 5a"), Is.EqualTo(("Winzerstraße", "5a")));
Assert.That(Utils.SplitAddress("Winzerstraße 1-3/2"), Is.EqualTo(("Winzerstraße", "1-3/2")));
Assert.That(Utils.SplitAddress("Winzerstraße 3/4/5"), Is.EqualTo(("Winzerstraße", "3/4/5")));
Assert.That(Utils.SplitAddress("Winzerstraße 7/2/4/77"), Is.EqualTo(("Winzerstraße", "7/2/4/77")));
Assert.That(Utils.SplitAddress("Winzerstraße 95b"), Is.EqualTo(("Winzerstraße", "95b")));
Assert.That(Utils.SplitAddress("Winzerstraße 1, TOP 3"), Is.EqualTo(("Winzerstraße", "1, TOP 3")));
});
}
[Test]
public void Test_SplitName() {
Assert.Multiple(() => {
Assert.That(Utils.SplitName("Max Bauer", "Bauer"), Is.EqualTo(("Bauer", "Max")));
Assert.That(Utils.SplitName("Bauer Max", "Bauer"), Is.EqualTo(("Bauer", "Max")));
Assert.That(Utils.SplitName("Max und Moritz Bauer", "Bauer"), Is.EqualTo(("Bauer", "Max und Moritz")));
Assert.That(Utils.SplitName("Bauer Max und Moritz", "Bauer"), Is.EqualTo(("Bauer", "Max und Moritz")));
Assert.That(Utils.SplitName("Bauer GesbR", "Bauer"), Is.EqualTo(("Bauer", "GesbR")));
Assert.That(Utils.SplitName("Max und Moritz Bauer GesbR", "Bauer"), Is.EqualTo(("Bauer", "Max und Moritz GesbR")));
Assert.That(Utils.SplitName("Bauer Max und Moritz GesbR", "Bauer"), Is.EqualTo(("Bauer", "Max und Moritz GesbR")));
Assert.That(Utils.SplitName("Weingut Bauer", "Bauer"), Is.EqualTo(("Bauer", "Weingut")));
Assert.That(Utils.SplitName("Bauer Weingut", "Bauer"), Is.EqualTo(("Bauer", "Weingut")));
Assert.That(Utils.SplitName("Max und Moritz Bauer und Mustermann", "Bauer"), Is.EqualTo(("Bauer und Mustermann", "Max und Moritz")));
Assert.That(Utils.SplitName("Bauer und Mustermann Max und Moritz", "Bauer"), Is.EqualTo(("Bauer und Mustermann", "Max und Moritz")));
Assert.That(Utils.SplitName("ABC GesbR", "Bauer"), Is.EqualTo(((string, string?))("ABC GesbR", null)));
});
}
} }
} }